diff --git a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h index c35d6242..ebdab4b7 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h @@ -479,5 +479,57 @@ namespace skyline::gpu::interconnect { void SetDepthBiasSlopeFactor(float factor) { rasterizerState.get().depthBiasSlopeFactor = factor; } + + /* Color Blending */ + private: + vk::PipelineColorBlendStateCreateInfo blendState{}; + + public: + void SetBlendLogicOpEnable(bool enabled) { + if (!gpu.quirks.supportsLogicOp && enabled) { + Logger::Warn("Cannot enable framebuffer logical operation without host GPU support"); + return; + } + blendState.logicOpEnable = enabled; + } + + void SetBlendLogicOpType(maxwell3d::ColorLogicOp logicOp) { + blendState.logicOp = [logicOp]() { + switch (logicOp) { + case maxwell3d::ColorLogicOp::Clear: + return vk::LogicOp::eClear; + case maxwell3d::ColorLogicOp::And: + return vk::LogicOp::eAnd; + case maxwell3d::ColorLogicOp::AndReverse: + return vk::LogicOp::eAndReverse; + case maxwell3d::ColorLogicOp::Copy: + return vk::LogicOp::eCopy; + case maxwell3d::ColorLogicOp::AndInverted: + return vk::LogicOp::eAndInverted; + case maxwell3d::ColorLogicOp::Noop: + return vk::LogicOp::eNoOp; + case maxwell3d::ColorLogicOp::Xor: + return vk::LogicOp::eXor; + case maxwell3d::ColorLogicOp::Or: + return vk::LogicOp::eOr; + case maxwell3d::ColorLogicOp::Nor: + return vk::LogicOp::eNor; + case maxwell3d::ColorLogicOp::Equiv: + return vk::LogicOp::eEquivalent; + case maxwell3d::ColorLogicOp::Invert: + return vk::LogicOp::eInvert; + case maxwell3d::ColorLogicOp::OrReverse: + return vk::LogicOp::eOrReverse; + case maxwell3d::ColorLogicOp::CopyInverted: + return vk::LogicOp::eCopyInverted; + case maxwell3d::ColorLogicOp::OrInverted: + return vk::LogicOp::eOrInverted; + case maxwell3d::ColorLogicOp::Nand: + return vk::LogicOp::eNand; + case maxwell3d::ColorLogicOp::Set: + return vk::LogicOp::eSet; + } + }(); + } }; } diff --git a/app/src/main/cpp/skyline/gpu/quirk_manager.cpp b/app/src/main/cpp/skyline/gpu/quirk_manager.cpp index f19a4966..528d74e0 100644 --- a/app/src/main/cpp/skyline/gpu/quirk_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/quirk_manager.cpp @@ -4,7 +4,7 @@ #include "quirk_manager.h" namespace skyline { - QuirkManager::QuirkManager(vk::PhysicalDeviceProperties properties, vk::PhysicalDeviceFeatures2 features, const std::vector &extensions) { + QuirkManager::QuirkManager(vk::PhysicalDeviceProperties properties, vk::PhysicalDeviceFeatures2 features2, const std::vector &extensions) { for (auto &extension : extensions) { #define EXT_SET(name, property) \ case util::Hash(name): \ @@ -27,5 +27,7 @@ namespace skyline { #undef EXT_SET #undef EXT_SET_V } + + supportsLogicOp = features2.features.logicOp; } } diff --git a/app/src/main/cpp/skyline/gpu/quirk_manager.h b/app/src/main/cpp/skyline/gpu/quirk_manager.h index d1133b1d..404e70fd 100644 --- a/app/src/main/cpp/skyline/gpu/quirk_manager.h +++ b/app/src/main/cpp/skyline/gpu/quirk_manager.h @@ -13,6 +13,7 @@ namespace skyline { class QuirkManager { public: bool supportsLastProvokingVertex{}; //!< If the device supports setting the last vertex as the provoking vertex (with VK_EXT_provoking_vertex) + bool supportsLogicOp{}; //!< If the device supports framebuffer logical operations during blending QuirkManager() = default; diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/types.h b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/types.h index 182c02f0..f6d59ecf 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/types.h +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/types.h @@ -540,6 +540,28 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type { }; static_assert(sizeof(SemaphoreInfo) == sizeof(u32)); + /** + * @brief The logical operations that can be performed on the framebuffer after the fragment shader + */ + enum class ColorLogicOp : u32 { + Clear = 0x1500, + And = 0x1501, + AndReverse = 0x1502, + Copy = 0x1503, + AndInverted = 0x1504, + Noop = 0x1505, + Xor = 0x1506, + Or = 0x1507, + Nor = 0x1508, + Equiv = 0x1509, + Invert = 0x150A, + OrReverse = 0x150B, + CopyInverted = 0x150C, + OrInverted = 0x150D, + Nand = 0x150E, + Set = 0x150F, + }; + constexpr static size_t StageCount{6}; //!< Amount of pipeline stages on Maxwell 3D /** diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp index e4d6361b..725bbf45 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp @@ -237,6 +237,14 @@ namespace skyline::soc::gm20b::engine::maxwell3d { context.SetDepthClampEnabled(!viewVolumeClipControl.depthClampDisable); }) + MAXWELL3D_STRUCT_CASE(colorLogicOp, enable, { + context.SetBlendLogicOpEnable(enable); + }) + + MAXWELL3D_STRUCT_CASE(colorLogicOp, type, { + context.SetBlendLogicOpType(type); + }) + #define SET_SHADER_ENABLE_CALLBACK(z, index, data) \ MAXWELL3D_ARRAY_STRUCT_CASE(setProgram, index, info, { \ context.SetShaderEnabled(info.stage, info.enable); \ diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h index e749b0b7..d5af0ee8 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h @@ -219,6 +219,12 @@ namespace skyline::soc::gm20b::engine::maxwell3d { }; Register<0x6C0, Semaphore> semaphore; + struct ColorLogicOp { + u32 enable; + type::ColorLogicOp type; + }; + Register<0x671, ColorLogicOp> colorLogicOp; + Register<0x780, std::array> independentBlend; Register<0x800, std::array> setProgram;