From c31a8f860bbc51d655e92e254254a783f925842f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 31 Dec 2015 01:50:18 -0800 Subject: [PATCH 1/4] Adjust stencil ops for 5551 and 565 buffers. This attempts to better approximate the operations that actually happen, where possible. Expected to help #5278. --- GPU/Common/GPUStateUtils.cpp | 122 +++++++++++++++++++++++++++++++ GPU/Common/GPUStateUtils.h | 13 ++++ GPU/Directx9/StateMappingDX9.cpp | 23 +++--- GPU/GLES/StateMapping.cpp | 24 +++--- 4 files changed, 153 insertions(+), 29 deletions(-) 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(); } From 64a34a6d92edd96abefcfe7c499d6d3b977a320b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 31 Dec 2015 10:27:02 -0800 Subject: [PATCH 2/4] Handle masking correctly for 5551 stencil rewrite. --- GPU/Common/GPUStateUtils.cpp | 156 +++++++++++++++++------------------ 1 file changed, 77 insertions(+), 79 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index b2d17e7f17..839e46fa83 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1137,6 +1137,17 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) { state.testMask = gstate.getStencilTestMask(); bool usesRef = state.sFail == GE_STENCILOP_REPLACE || state.zFail == GE_STENCILOP_REPLACE || state.zPass == GE_STENCILOP_REPLACE; + u8 maskedRef = state.testRef & state.testMask; + + auto rewriteFunc = [&](GEComparison func, u8 ref, u8 mask = 0xFF) { + // We can only safely rewrite if it doesn't use the ref, or if the ref is the same. + if (!usesRef || maskedRef == ref) { + state.testFunc = func; + state.testRef = ref; + state.testMask = mask; + } + }; + switch (gstate.FrameBufFormat()) { case GE_FORMAT_565: state.writeMask = 0; @@ -1153,86 +1164,73 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) { 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; + // 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: // maskedRef == maskedBuffer + if (maskedRef == 0) { + // Remove any mask, we might have bits less than 255 but that should not match. + rewriteFunc(GE_COMP_EQUAL, 0); + } else if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { + // Equal to 255, for our buffer, means not equal to zero. + rewriteFunc(GE_COMP_NOTEQUAL, 0); + } else { + // 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: // maskedRef != maskedBuffer + if (maskedRef == 0) { + // Remove the mask, since our buffer might not be exactly 255. + rewriteFunc(GE_COMP_NOTEQUAL, 0); + } else if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { + // The only value != 255 is 0, in our buffer. + rewriteFunc(GE_COMP_EQUAL, 0); + } else { + // Every other value evaluates as not equal, always. + state.testFunc = GE_COMP_ALWAYS; + } + break; + case GE_COMP_LESS: // maskedRef < maskedBuffer + if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { + // No possible value is less than 255. + state.testFunc = GE_COMP_NEVER; + } else { + // "0 < (0 or 255)" and "254 < (0 or 255)" can only work for non zero. + rewriteFunc(GE_COMP_NOTEQUAL, 0); + } + break; + case GE_COMP_LEQUAL: // maskedRef <= maskedBuffer + if (maskedRef == 0) { + // 0 is <= every possible value. + state.testFunc = GE_COMP_ALWAYS; + } else { + // "1 <= (0 or 255)" and "255 <= (0 or 255)" simply mean, anything but zero. + rewriteFunc(GE_COMP_NOTEQUAL, 0); + } + break; + case GE_COMP_GREATER: // maskedRef > maskedBuffer + if (maskedRef > 0) { + // "1 > (0 or 255)" and "255 > (0 or 255)" can only match 0. + rewriteFunc(GE_COMP_EQUAL, 0); + } else { + // 0 is never greater than any possible value. + state.testFunc = GE_COMP_NEVER; + } + break; + case GE_COMP_GEQUAL: // maskedRef >= maskedBuffer + if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { + // 255 is >= every possible value. + state.testFunc = GE_COMP_ALWAYS; + } else { + // "0 >= (0 or 255)" and "254 >= "(0 or 255)" are the same, equal to zero. + rewriteFunc(GE_COMP_EQUAL, 0); + } + break; } break; From e1599821ea70623e87adf829c43cb8ea0313ad2c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 31 Dec 2015 17:12:12 -0800 Subject: [PATCH 3/4] Split out the 5551 logic to be clearer. --- GPU/Common/GPUStateUtils.cpp | 190 ++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 90 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 839e46fa83..a8e0c7d7a3 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1120,6 +1120,105 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) { } } +static void ConvertStencilFunc5551(GenericStencilFuncState &state) { + // 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; + + state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00; + + // Flaws: + // - INVERT should convert 1, 5, 0xFF to 0. Currently it won't. + // - INCR twice shouldn't change the value. + // - REPLACE should write 0 for 0x00 - 0x7F, and non-zero for 0x80 - 0xFF. + // - Write mask may need double checking, but likely only the top bit matters. + + const bool usesRef = state.sFail == GE_STENCILOP_REPLACE || state.zFail == GE_STENCILOP_REPLACE || state.zPass == GE_STENCILOP_REPLACE; + const u8 maskedRef = state.testRef & state.testMask; + + auto rewriteFunc = [&](GEComparison func, u8 ref, u8 mask = 0xFF) { + // We can only safely rewrite if it doesn't use the ref, or if the ref is the same. + if (!usesRef || maskedRef == ref) { + state.testFunc = func; + state.testRef = ref; + state.testMask = mask; + } + }; + + // 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: // maskedRef == maskedBuffer + if (maskedRef == 0) { + // Remove any mask, we might have bits less than 255 but that should not match. + rewriteFunc(GE_COMP_EQUAL, 0); + } else if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { + // Equal to 255, for our buffer, means not equal to zero. + rewriteFunc(GE_COMP_NOTEQUAL, 0); + } else { + // 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: // maskedRef != maskedBuffer + if (maskedRef == 0) { + // Remove the mask, since our buffer might not be exactly 255. + rewriteFunc(GE_COMP_NOTEQUAL, 0); + } else if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { + // The only value != 255 is 0, in our buffer. + rewriteFunc(GE_COMP_EQUAL, 0); + } else { + // Every other value evaluates as not equal, always. + state.testFunc = GE_COMP_ALWAYS; + } + break; + case GE_COMP_LESS: // maskedRef < maskedBuffer + if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { + // No possible value is less than 255. + state.testFunc = GE_COMP_NEVER; + } else { + // "0 < (0 or 255)" and "254 < (0 or 255)" can only work for non zero. + rewriteFunc(GE_COMP_NOTEQUAL, 0); + } + break; + case GE_COMP_LEQUAL: // maskedRef <= maskedBuffer + if (maskedRef == 0) { + // 0 is <= every possible value. + state.testFunc = GE_COMP_ALWAYS; + } else { + // "1 <= (0 or 255)" and "255 <= (0 or 255)" simply mean, anything but zero. + rewriteFunc(GE_COMP_NOTEQUAL, 0); + } + break; + case GE_COMP_GREATER: // maskedRef > maskedBuffer + if (maskedRef > 0) { + // "1 > (0 or 255)" and "255 > (0 or 255)" can only match 0. + rewriteFunc(GE_COMP_EQUAL, 0); + } else { + // 0 is never greater than any possible value. + state.testFunc = GE_COMP_NEVER; + } + break; + case GE_COMP_GEQUAL: // maskedRef >= maskedBuffer + if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { + // 255 is >= every possible value. + state.testFunc = GE_COMP_ALWAYS; + } else { + // "0 >= (0 or 255)" and "254 >= "(0 or 255)" are the same, equal to zero. + rewriteFunc(GE_COMP_EQUAL, 0); + } + break; + } +} + void ConvertStencilFuncState(GenericStencilFuncState &state) { state.enabled = gstate.isStencilTestEnabled() && !g_Config.bDisableStencilTest; if (!state.enabled) @@ -1136,102 +1235,13 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) { 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; - u8 maskedRef = state.testRef & state.testMask; - - auto rewriteFunc = [&](GEComparison func, u8 ref, u8 mask = 0xFF) { - // We can only safely rewrite if it doesn't use the ref, or if the ref is the same. - if (!usesRef || maskedRef == ref) { - state.testFunc = func; - state.testRef = ref; - state.testMask = mask; - } - }; - 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; - - // 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: // maskedRef == maskedBuffer - if (maskedRef == 0) { - // Remove any mask, we might have bits less than 255 but that should not match. - rewriteFunc(GE_COMP_EQUAL, 0); - } else if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { - // Equal to 255, for our buffer, means not equal to zero. - rewriteFunc(GE_COMP_NOTEQUAL, 0); - } else { - // 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: // maskedRef != maskedBuffer - if (maskedRef == 0) { - // Remove the mask, since our buffer might not be exactly 255. - rewriteFunc(GE_COMP_NOTEQUAL, 0); - } else if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { - // The only value != 255 is 0, in our buffer. - rewriteFunc(GE_COMP_EQUAL, 0); - } else { - // Every other value evaluates as not equal, always. - state.testFunc = GE_COMP_ALWAYS; - } - break; - case GE_COMP_LESS: // maskedRef < maskedBuffer - if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { - // No possible value is less than 255. - state.testFunc = GE_COMP_NEVER; - } else { - // "0 < (0 or 255)" and "254 < (0 or 255)" can only work for non zero. - rewriteFunc(GE_COMP_NOTEQUAL, 0); - } - break; - case GE_COMP_LEQUAL: // maskedRef <= maskedBuffer - if (maskedRef == 0) { - // 0 is <= every possible value. - state.testFunc = GE_COMP_ALWAYS; - } else { - // "1 <= (0 or 255)" and "255 <= (0 or 255)" simply mean, anything but zero. - rewriteFunc(GE_COMP_NOTEQUAL, 0); - } - break; - case GE_COMP_GREATER: // maskedRef > maskedBuffer - if (maskedRef > 0) { - // "1 > (0 or 255)" and "255 > (0 or 255)" can only match 0. - rewriteFunc(GE_COMP_EQUAL, 0); - } else { - // 0 is never greater than any possible value. - state.testFunc = GE_COMP_NEVER; - } - break; - case GE_COMP_GEQUAL: // maskedRef >= maskedBuffer - if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { - // 255 is >= every possible value. - state.testFunc = GE_COMP_ALWAYS; - } else { - // "0 >= (0 or 255)" and "254 >= "(0 or 255)" are the same, equal to zero. - rewriteFunc(GE_COMP_EQUAL, 0); - } - break; - } + ConvertStencilFunc5551(state); break; default: From fd2e302a6688abfe6aa0f423540fb29565818f19 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 31 Dec 2015 17:16:41 -0800 Subject: [PATCH 4/4] When possible, replace 5551 stencil with 0xFF. --- GPU/Common/GPUStateUtils.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index a8e0c7d7a3..da5ef117a9 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1139,15 +1139,23 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { const bool usesRef = state.sFail == GE_STENCILOP_REPLACE || state.zFail == GE_STENCILOP_REPLACE || state.zPass == GE_STENCILOP_REPLACE; const u8 maskedRef = state.testRef & state.testMask; + const u8 usedRef = (state.testRef & 0x80) != 0 ? 0xFF : 0x00; auto rewriteFunc = [&](GEComparison func, u8 ref, u8 mask = 0xFF) { // We can only safely rewrite if it doesn't use the ref, or if the ref is the same. - if (!usesRef || maskedRef == ref) { + if (!usesRef || usedRef == ref) { state.testFunc = func; state.testRef = ref; state.testMask = mask; } }; + auto rewriteRef = [&]() { + if (usesRef) { + // Rewrite the ref (for REPLACE) to 0x00 or 0xFF (the "best" values) if safe. + // This will only be called if the test doesn't need the ref. + state.testRef = usedRef; + } + }; // 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 @@ -1155,6 +1163,7 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { case GE_COMP_NEVER: case GE_COMP_ALWAYS: // Fine as is. + rewriteRef(); break; case GE_COMP_EQUAL: // maskedRef == maskedBuffer if (maskedRef == 0) { @@ -1166,6 +1175,7 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { } else { // This should never pass, regardless of buffer value. Only 0 and 255 are directly equal. state.testFunc = GE_COMP_NEVER; + rewriteRef(); } break; case GE_COMP_NOTEQUAL: // maskedRef != maskedBuffer @@ -1178,12 +1188,14 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { } else { // Every other value evaluates as not equal, always. state.testFunc = GE_COMP_ALWAYS; + rewriteRef(); } break; case GE_COMP_LESS: // maskedRef < maskedBuffer if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { // No possible value is less than 255. state.testFunc = GE_COMP_NEVER; + rewriteRef(); } else { // "0 < (0 or 255)" and "254 < (0 or 255)" can only work for non zero. rewriteFunc(GE_COMP_NOTEQUAL, 0); @@ -1193,6 +1205,7 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { if (maskedRef == 0) { // 0 is <= every possible value. state.testFunc = GE_COMP_ALWAYS; + rewriteRef(); } else { // "1 <= (0 or 255)" and "255 <= (0 or 255)" simply mean, anything but zero. rewriteFunc(GE_COMP_NOTEQUAL, 0); @@ -1205,12 +1218,14 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { } else { // 0 is never greater than any possible value. state.testFunc = GE_COMP_NEVER; + rewriteRef(); } break; case GE_COMP_GEQUAL: // maskedRef >= maskedBuffer if (maskedRef == (0xFF & state.testMask) && state.testMask != 0) { // 255 is >= every possible value. state.testFunc = GE_COMP_ALWAYS; + rewriteRef(); } else { // "0 >= (0 or 255)" and "254 >= "(0 or 255)" are the same, equal to zero. rewriteFunc(GE_COMP_EQUAL, 0);