GPU: Respect stencil write mask for 5551 buffers.

If the mask is 0x7F on 5551, that's equivalent to allowing the clear
entirely.  See #13391.
This commit is contained in:
Unknown W. Brackets 2021-01-24 15:54:13 -08:00
parent a88f6b45d2
commit 9857e8d1b1
5 changed files with 28 additions and 22 deletions

View file

@ -1303,8 +1303,6 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead)
}
static void ConvertStencilFunc5551(GenericStencilFuncState &state) {
state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00;
// Flaws:
// - INVERT should convert 1, 5, 0xFF to 0. Currently it won't always.
// - INCR twice shouldn't change the value.
@ -1436,13 +1434,19 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) {
}
}
void ConvertStencilFuncState(GenericStencilFuncState &state) {
state.enabled = gstate.isStencilTestEnabled();
if (!state.enabled)
return;
static void ConvertStencilMask5551(GenericStencilFuncState &state) {
state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00;
}
// The PSP's mask is reversed (bits not to write.)
void ConvertStencilFuncState(GenericStencilFuncState &state) {
// The PSP's mask is reversed (bits not to write.) Ignore enabled, used for clears too.
state.writeMask = (~gstate.getStencilWriteMask()) & 0xFF;
state.enabled = gstate.isStencilTestEnabled();
if (!state.enabled) {
if (gstate.FrameBufFormat() == GE_FORMAT_5551)
ConvertStencilMask5551(state);
return;
}
state.sFail = gstate.getStencilOpSFail();
state.zFail = gstate.getStencilOpZFail();
@ -1458,6 +1462,7 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) {
break;
case GE_FORMAT_5551:
ConvertStencilMask5551(state);
ConvertStencilFunc5551(state);
break;

View file

@ -284,6 +284,9 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
}
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
if (gstate.isModeClear()) {
keys_.depthStencil.value = 0;
keys_.depthStencil.depthTestEnable = true;
@ -307,7 +310,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
// We override this value in the pipeline from software transform for clear rectangles.
dynState_.stencilRef = 0xFF;
// But we still apply the stencil write mask.
keys_.depthStencil.stencilWriteMask = (~gstate.getStencilWriteMask()) & 0xFF;
keys_.depthStencil.stencilWriteMask = stencilState.writeMask;
} else {
keys_.depthStencil.stencilTestEnable = false;
dynState_.useStencil = false;
@ -329,9 +332,6 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
keys_.depthStencil.depthCompareOp = D3D11_COMPARISON_ALWAYS;
}
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
// Stencil Test
if (stencilState.enabled) {
keys_.depthStencil.stencilTestEnable = true;

View file

@ -187,6 +187,9 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
gstate_c.Clean(DIRTY_DEPTHSTENCIL_STATE);
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
// Set Stencil/Depth
if (gstate.isModeClear()) {
// Depth Test
@ -203,7 +206,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
dxstate.stencilTest.enable();
dxstate.stencilOp.set(D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE);
dxstate.stencilFunc.set(D3DCMP_ALWAYS, 255, 0xFF);
dxstate.stencilMask.set((~gstate.getStencilWriteMask()) & 0xFF);
dxstate.stencilMask.set(stencilState.writeMask);
} else {
dxstate.stencilTest.disable();
}
@ -221,9 +224,6 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
dxstate.depthTest.disable();
}
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
// Stencil Test
if (stencilState.enabled) {
dxstate.stencilTest.enable();

View file

@ -235,13 +235,16 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
gstate_c.Clean(DIRTY_DEPTHSTENCIL_STATE);
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
if (gstate.isModeClear()) {
// Depth Test
if (gstate.isClearModeDepthMask()) {
framebufferManager_->SetDepthUpdated();
}
renderManager->SetStencilFunc(gstate.isClearModeAlphaMask(), GL_ALWAYS, 0xFF, 0xFF);
renderManager->SetStencilOp((~gstate.getStencilWriteMask()) & 0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE);
renderManager->SetStencilOp(stencilState.writeMask, GL_REPLACE, GL_REPLACE, GL_REPLACE);
renderManager->SetDepth(true, gstate.isClearModeDepthMask() ? true : false, GL_ALWAYS);
} else {
// Depth Test
@ -250,8 +253,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
framebufferManager_->SetDepthUpdated();
}
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
// Stencil Test
if (stencilState.enabled) {
renderManager->SetStencilFunc(stencilState.enabled, compareOps[stencilState.testFunc], stencilState.testRef, stencilState.testMask);

View file

@ -253,6 +253,9 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
}
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
if (gstate.isModeClear()) {
key.depthTestEnable = true;
key.depthCompareOp = VK_COMPARE_OP_ALWAYS;
@ -275,7 +278,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
// We override this value in the pipeline from software transform for clear rectangles.
dynState.stencilRef = 0xFF;
// But we still apply the stencil write mask.
dynState.stencilWriteMask = (~gstate.getStencilWriteMask()) & 0xFF;
dynState.stencilWriteMask = stencilState.writeMask;
} else {
key.stencilTestEnable = false;
key.stencilCompareOp = VK_COMPARE_OP_ALWAYS;
@ -299,9 +302,6 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
key.depthCompareOp = VK_COMPARE_OP_ALWAYS;
}
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
// Stencil Test
if (stencilState.enabled) {
key.stencilTestEnable = true;