Adjust stencil ops for 5551 and 565 buffers.

This attempts to better approximate the operations that actually happen,
where possible.  Expected to help #5278.
This commit is contained in:
Unknown W. Brackets 2015-12-31 01:50:18 -08:00
parent 337f27d0d9
commit c31a8f860b
4 changed files with 153 additions and 29 deletions

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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();
}