diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index b2f46e0d09..b2d17e7f17 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1119,3 +1119,125 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) { blendState.setEquation(eqLookupNoMinMax[blendFuncEq], alphaEq); } } + +void ConvertStencilFuncState(GenericStencilFuncState &state) { + state.enabled = gstate.isStencilTestEnabled() && !g_Config.bDisableStencilTest; + if (!state.enabled) + return; + + // The PSP's mask is reversed (bits not to write.) + state.writeMask = (~(gstate.pmska >> 0)) & 0xFF; + + state.sFail = gstate.getStencilOpSFail(); + state.zFail = gstate.getStencilOpZFail(); + state.zPass = gstate.getStencilOpZPass(); + + state.testFunc = gstate.getStencilTestFunction(); + state.testRef = gstate.getStencilTestRef(); + state.testMask = gstate.getStencilTestMask(); + + bool usesRef = state.sFail == GE_STENCILOP_REPLACE || state.zFail == GE_STENCILOP_REPLACE || state.zPass == GE_STENCILOP_REPLACE; + switch (gstate.FrameBufFormat()) { + case GE_FORMAT_565: + state.writeMask = 0; + break; + + case GE_FORMAT_5551: + state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00; + + // Decrement always zeros, so let's rewrite those to be safe (even if it's not 1.) + if (state.sFail == GE_STENCILOP_DECR) + state.sFail = GE_STENCILOP_ZERO; + if (state.zFail == GE_STENCILOP_DECR) + state.zFail = GE_STENCILOP_ZERO; + if (state.zPass == GE_STENCILOP_DECR) + state.zPass = GE_STENCILOP_ZERO; + + if (!usesRef) { + // For 5551, we treat any non-zero value in the buffer as 255. Only zero is treated as zero. + // See: https://github.com/hrydgard/ppsspp/pull/4150#issuecomment-26211193 + switch (state.testFunc) { + case GE_COMP_NEVER: + case GE_COMP_ALWAYS: + // Fine as is. + break; + case GE_COMP_EQUAL: + if (state.testRef == 255) { + if (!usesRef) { + state.testFunc = GE_COMP_NOTEQUAL; + state.testRef = 0; + state.testMask = 255; + } + } else if (state.testRef != 0) { + // This should never pass, regardless of buffer value. Only 0 and 255 are directly equal. + state.testFunc = GE_COMP_NEVER; + } + break; + case GE_COMP_NOTEQUAL: + if (state.testRef == 255) { + if (!usesRef) { + state.testFunc = GE_COMP_EQUAL; + state.testRef = 0; + state.testMask = 255; + } + } else if (state.testRef != 0) { + state.testFunc = GE_COMP_ALWAYS; + } + break; + case GE_COMP_LESS: + if (state.testRef == 255) { + state.testFunc = GE_COMP_NEVER; + } else { + if (!usesRef || state.testRef == 0) { + // Any non-zero value in the buffer should be treated as 255. + state.testFunc = GE_COMP_NOTEQUAL; + state.testRef = 0; + state.testMask = 255; + } + } + break; + case GE_COMP_LEQUAL: + if (state.testRef == 0) { + state.testFunc = GE_COMP_ALWAYS; + } else { + if (!usesRef) { + // Everything but 0 in the buffer is "255", so only 0 doesn't match. + state.testFunc = GE_COMP_NOTEQUAL; + state.testRef = 0; + state.testMask = 255; + } + } + break; + case GE_COMP_GREATER: + if (state.testRef > 0) { + if (!usesRef) { + // If we have a 1 in the buffer, it's the same as 255, which > can never match. + state.testFunc = GE_COMP_EQUAL; + state.testRef = 0; + state.testMask = 255; + } + } else { + state.testFunc = GE_COMP_NEVER; + } + break; + case GE_COMP_GEQUAL: + if (state.testRef == 255) { + state.testFunc = GE_COMP_ALWAYS; + } else { + if (!usesRef || state.testRef == 0) { + // If the buffer is non-zero, we treat as 255. So only 0 can match. + state.testFunc = GE_COMP_EQUAL; + state.testRef = 0; + state.testMask = 255; + } + } + break; + } + } + break; + + default: + // Hard to do anything useful for 4444, and 8888 is fine. + break; + } +} diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index f7a2c53685..b3c398db11 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -137,3 +137,16 @@ struct GenericBlendState { void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend); void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState); + +struct GenericStencilFuncState { + bool enabled; + GEComparison testFunc; + u8 testRef; + u8 testMask; + u8 writeMask; + GEStencilOp sFail; + GEStencilOp zFail; + GEStencilOp zPass; +}; + +void ConvertStencilFuncState(GenericStencilFuncState &stencilFuncState); diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 94e9df4841..feb008488d 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -243,8 +243,8 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { bool bmask = ((gstate.pmskc >> 16) & 0xFF) < 128; bool amask = (gstate.pmska & 0xFF) < 128; - u8 abits = (gstate.pmska >> 0) & 0xFF; #ifndef MOBILE_DEVICE + u8 abits = (gstate.pmska >> 0) & 0xFF; u8 rbits = (gstate.pmskc >> 0) & 0xFF; u8 gbits = (gstate.pmskc >> 8) & 0xFF; u8 bbits = (gstate.pmskc >> 16) & 0xFF; @@ -268,21 +268,16 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { } dxstate.colorMask.set(rmask, gmask, bmask, amask); - + + GenericStencilFuncState stencilState; + ConvertStencilFuncState(stencilState); + // Stencil Test - if (gstate.isStencilTestEnabled()) { + if (stencilState.enabled) { dxstate.stencilTest.enable(); - dxstate.stencilFunc.set(ztests[gstate.getStencilTestFunction()], - gstate.getStencilTestRef(), - gstate.getStencilTestMask()); - dxstate.stencilOp.set(stencilOps[gstate.getStencilOpSFail()], // stencil fail - stencilOps[gstate.getStencilOpZFail()], // depth fail - stencilOps[gstate.getStencilOpZPass()]); // depth pass - if (gstate.FrameBufFormat() == GE_FORMAT_5551) { - dxstate.stencilMask.set(abits <= 0x7f ? 0xff : 0x00); - } else { - dxstate.stencilMask.set(~abits); - } + dxstate.stencilFunc.set(ztests[stencilState.testFunc], stencilState.testRef, stencilState.testMask); + dxstate.stencilOp.set(stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]); + dxstate.stencilMask.set(stencilState.writeMask); } else { dxstate.stencilTest.disable(); } diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 840803ba9c..fb433472f5 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -312,8 +312,8 @@ void TransformDrawEngine::ApplyDrawState(int prim) { bool bmask = ((gstate.pmskc >> 16) & 0xFF) < 128; bool amask = (gstate.pmska & 0xFF) < 128; - u8 abits = (gstate.pmska >> 0) & 0xFF; #ifndef MOBILE_DEVICE + u8 abits = (gstate.pmska >> 0) & 0xFF; u8 rbits = (gstate.pmskc >> 0) & 0xFF; u8 gbits = (gstate.pmskc >> 8) & 0xFF; u8 bbits = (gstate.pmskc >> 16) & 0xFF; @@ -338,21 +338,15 @@ void TransformDrawEngine::ApplyDrawState(int prim) { glstate.colorMask.set(rmask, gmask, bmask, amask); - // Stencil Test - if (gstate.isStencilTestEnabled() && enableStencilTest) { - glstate.stencilTest.enable(); - glstate.stencilFunc.set(ztests[gstate.getStencilTestFunction()], - gstate.getStencilTestRef(), - gstate.getStencilTestMask()); - glstate.stencilOp.set(stencilOps[gstate.getStencilOpSFail()], // stencil fail - stencilOps[gstate.getStencilOpZFail()], // depth fail - stencilOps[gstate.getStencilOpZPass()]); // depth pass + GenericStencilFuncState stencilState; + ConvertStencilFuncState(stencilState); - if (gstate.FrameBufFormat() == GE_FORMAT_5551) { - glstate.stencilMask.set(abits <= 0x7f ? 0xff : 0x00); - } else { - glstate.stencilMask.set(~abits); - } + // Stencil Test + if (stencilState.enabled) { + glstate.stencilTest.enable(); + glstate.stencilFunc.set(ztests[stencilState.testFunc], stencilState.testRef, stencilState.testMask); + glstate.stencilOp.set(stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]); + glstate.stencilMask.set(stencilState.writeMask); } else { glstate.stencilTest.disable(); }