diff --git a/Source/Core/VideoCommon/RenderState.cpp b/Source/Core/VideoCommon/RenderState.cpp index 04d8804f0c..6d6f892979 100644 --- a/Source/Core/VideoCommon/RenderState.cpp +++ b/Source/Core/VideoCommon/RenderState.cpp @@ -167,6 +167,42 @@ void BlendingState::Generate(const BPMemory& bp) } } +void BlendingState::ApproximateLogicOpWithBlending() +{ + // Any of these which use SRC as srcFactor or DST as dstFactor won't be correct. + // This is because the two are aliased to one another (see the enum). + struct LogicOpApproximation + { + bool subtract; + BlendMode::BlendFactor srcfactor; + BlendMode::BlendFactor dstfactor; + }; + static constexpr std::array approximations = {{ + {false, BlendMode::ZERO, BlendMode::ZERO}, // CLEAR + {false, BlendMode::DSTCLR, BlendMode::ZERO}, // AND + {true, BlendMode::ONE, BlendMode::INVSRCCLR}, // AND_REVERSE + {false, BlendMode::ONE, BlendMode::ZERO}, // COPY + {true, BlendMode::DSTCLR, BlendMode::ONE}, // AND_INVERTED + {false, BlendMode::ZERO, BlendMode::ONE}, // NOOP + {false, BlendMode::INVDSTCLR, BlendMode::INVSRCCLR}, // XOR + {false, BlendMode::INVDSTCLR, BlendMode::ONE}, // OR + {false, BlendMode::INVSRCCLR, BlendMode::INVDSTCLR}, // NOR + {false, BlendMode::INVSRCCLR, BlendMode::ZERO}, // EQUIV + {false, BlendMode::INVDSTCLR, BlendMode::INVDSTCLR}, // INVERT + {false, BlendMode::ONE, BlendMode::INVDSTALPHA}, // OR_REVERSE + {false, BlendMode::INVSRCCLR, BlendMode::INVSRCCLR}, // COPY_INVERTED + {false, BlendMode::INVSRCCLR, BlendMode::ONE}, // OR_INVERTED + {false, BlendMode::INVDSTCLR, BlendMode::INVSRCCLR}, // NAND + {false, BlendMode::ONE, BlendMode::ONE}, // SET + }}; + + logicopenable = false; + blendenable = true; + subtract = approximations[logicmode].subtract; + srcfactor = approximations[logicmode].srcfactor; + dstfactor = approximations[logicmode].dstfactor; +} + BlendingState& BlendingState::operator=(const BlendingState& rhs) { hex = rhs.hex; diff --git a/Source/Core/VideoCommon/RenderState.h b/Source/Core/VideoCommon/RenderState.h index dc0676ccc4..f5bf085d72 100644 --- a/Source/Core/VideoCommon/RenderState.h +++ b/Source/Core/VideoCommon/RenderState.h @@ -68,6 +68,10 @@ union BlendingState { void Generate(const BPMemory& bp); + // HACK: Replaces logical operations with blend operations. + // Will not be bit-correct, and in some cases not even remotely in the same ballpark. + void ApproximateLogicOpWithBlending(); + BlendingState& operator=(const BlendingState& rhs); bool operator==(const BlendingState& rhs) const { return hex == rhs.hex; } diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 3d4f286b68..e3066520fb 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -558,6 +558,13 @@ AbstractPipelineConfig ShaderCache::GetGXPipelineConfig( config.depth_state = depth_state; config.blending_state = blending_state; config.framebuffer_state = g_framebuffer_manager->GetEFBFramebufferState(); + + if (config.blending_state.logicopenable && !g_ActiveConfig.backend_info.bSupportsLogicOp) + { + WARN_LOG(VIDEO, "Approximating logic op with blending, this will produce incorrect rendering."); + config.blending_state.ApproximateLogicOpWithBlending(); + } + return config; }