Implement the stencil/alpha reverse trick for all backends

This commit is contained in:
Henrik Rydgård 2022-08-27 10:05:57 +02:00
parent 59053e7815
commit 880ea48e2d
4 changed files with 96 additions and 6 deletions

View file

@ -293,6 +293,34 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
keys_.depthStencil.stencilWriteMask = stencilState.writeMask;
dynState_.useStencil = true;
dynState_.stencilRef = stencilState.testRef;
// Nasty special case for Spongebob and similar where it tries to write zeros to alpha/stencil during
// depth-fail. We can't write to alpha then because the pixel is killed. However, we can invert the depth
// test and modify the alpha function...
if (gstate.isDepthTestEnabled() && !gstate.isDepthWriteEnabled() && stencilState.zFail == GE_STENCILOP_ZERO &&
stencilState.sFail == GE_STENCILOP_KEEP && stencilState.zPass == GE_STENCILOP_KEEP &&
stencilState.testFunc == GE_COMP_ALWAYS &&
stencilState.writeMask == 0xFF && stencilState.testMask == 0xFF && stencilState.testRef == 0xFF) {
keys_.blend.blendEnable = true;
keys_.blend.blendOpAlpha = D3D11_BLEND_OP_ADD;
keys_.blend.blendOpColor = D3D11_BLEND_OP_ADD;
keys_.blend.srcColor = D3D11_BLEND_ZERO;
keys_.blend.destColor = D3D11_BLEND_ZERO;
keys_.blend.logicOpEnable = false;
keys_.blend.srcAlpha = D3D11_BLEND_ZERO;
keys_.blend.destAlpha = D3D11_BLEND_ZERO;
keys_.blend.colorWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA;
keys_.depthStencil.depthCompareOp = D3D11_COMPARISON_LESS; // Inverse of GREATER_EQUAL
keys_.depthStencil.stencilCompareFunc = D3D11_COMPARISON_ALWAYS;
// Invert
keys_.depthStencil.stencilPassOp = D3D11_STENCIL_OP_ZERO;
keys_.depthStencil.stencilFailOp = D3D11_STENCIL_OP_ZERO;
keys_.depthStencil.stencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
// TODO: Need to set in a way that carries over to the next draw..
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
} else {
keys_.depthStencil.stencilTestEnable = false;
dynState_.useStencil = false;

View file

@ -247,6 +247,28 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
dxstate.stencilCompareMask.set(stencilState.testMask);
dxstate.stencilOp.set(stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]);
dxstate.stencilWriteMask.set(stencilState.writeMask);
// Nasty special case for Spongebob and similar where it tries to write zeros to alpha/stencil during
// depth-fail. We can't write to alpha then because the pixel is killed. However, we can invert the depth
// test and modify the alpha function...
if (gstate.isDepthTestEnabled() && !gstate.isDepthWriteEnabled() && stencilState.zFail == GE_STENCILOP_ZERO &&
stencilState.sFail == GE_STENCILOP_KEEP && stencilState.zPass == GE_STENCILOP_KEEP &&
stencilState.testFunc == GE_COMP_ALWAYS &&
stencilState.writeMask == 0xFF && stencilState.testMask == 0xFF && stencilState.testRef == 0xFF) {
dxstate.blend.set(true);
dxstate.blendEquation.set(D3DBLENDOP_ADD, D3DBLENDOP_ADD);
dxstate.blendFunc.set(D3DBLEND_ZERO, D3DBLEND_ZERO, D3DBLEND_ZERO, D3DBLEND_ZERO);
dxstate.colorMask.set(8);
dxstate.depthFunc.set(D3DCMP_LESS);
dxstate.stencilFunc.set(D3DCMP_ALWAYS);
// Invert
dxstate.stencilOp.set(D3DSTENCILOP_ZERO, D3DSTENCILOP_KEEP, D3DSTENCILOP_ZERO);
// TODO: Need to set in a way that carries over to the next draw..
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
} else {
dxstate.stencilTest.disable();
}

View file

@ -144,7 +144,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
bool useBufferedRendering = framebufferManager_->UseBufferedRendering();
if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
gstate_c.Clean(DIRTY_BLEND_STATE);
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects);
if (gstate.isModeClear()) {
@ -208,7 +207,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
} else {
renderManager->SetNoBlendAndMask(mask);
}
#ifndef USING_GLES2
if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
renderManager->SetLogicOp(gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY,
@ -219,8 +217,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
}
if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) {
gstate_c.Clean(DIRTY_RASTER_STATE);
// Dither
bool dither = gstate.isDitherEnabled();
bool cullEnable;
@ -247,7 +243,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
}
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
gstate_c.Clean(DIRTY_DEPTHSTENCIL_STATE);
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
@ -265,13 +260,29 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
renderManager->SetStencilFunc(stencilState.enabled, compareOps[stencilState.testFunc], stencilState.testRef, stencilState.testMask);
renderManager->SetStencilOp(stencilState.writeMask, stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]);
} else {
// Nasty special case for Spongebob and similar where it tries to write zeros to alpha/stencil during
// depth-fail. We can't write to alpha then because the pixel is killed. However, we can invert the depth
// test and modify the alpha function...
if (gstate.isDepthTestEnabled() && !gstate.isDepthWriteEnabled() && stencilState.zFail == GE_STENCILOP_ZERO &&
stencilState.sFail == GE_STENCILOP_KEEP && stencilState.zPass == GE_STENCILOP_KEEP &&
stencilState.testFunc == GE_COMP_ALWAYS &&
stencilState.writeMask == 0xFF && stencilState.testMask == 0xFF && stencilState.testRef == 0xFF) {
renderManager->SetBlendAndMask(0x8, true, GL_ZERO, GL_ZERO, GL_ZERO, GL_ZERO, GL_ADD, GL_ADD);
renderManager->SetDepth(true, false, GL_LESS);
renderManager->SetStencilFunc(true, GL_ALWAYS, 0xFF, 0xFF);
renderManager->SetStencilOp(0xFF, GL_ZERO, GL_KEEP, GL_ZERO);
// TODO: Need to set in a way that carries over to the next draw..
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
renderManager->SetStencilDisabled();
}
}
}
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE);
ConvertViewportAndScissor(useBufferedRendering,
framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
@ -284,6 +295,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
vpAndScissor.viewportW, vpAndScissor.viewportH,
vpAndScissor.depthRangeMin, vpAndScissor.depthRangeMax });
}
gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_BLEND_STATE);
}
void DrawEngineGLES::ApplyDrawStateLate(bool setStencilValue, int stencilValue) {

View file

@ -301,6 +301,33 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
dynState.stencilRef = stencilState.testRef;
dynState.stencilCompareMask = stencilState.testMask;
dynState.stencilWriteMask = stencilState.writeMask;
// Nasty special case for Spongebob and similar where it tries to write zeros to alpha/stencil during
// depth-fail. We can't write to alpha then because the pixel is killed. However, we can invert the depth
// test and modify the alpha function...
if (gstate.isDepthTestEnabled() && !gstate.isDepthWriteEnabled() && stencilState.zFail == GE_STENCILOP_ZERO &&
stencilState.sFail == GE_STENCILOP_KEEP && stencilState.zPass == GE_STENCILOP_KEEP &&
stencilState.testFunc == GE_COMP_ALWAYS &&
stencilState.writeMask == 0xFF && stencilState.testMask == 0xFF && stencilState.testRef == 0xFF) {
key.blendEnable = true;
key.blendOpAlpha = VK_BLEND_OP_ADD;
key.blendOpColor = VK_BLEND_OP_ADD;
key.srcColor = VK_BLEND_FACTOR_ZERO;
key.destColor = VK_BLEND_FACTOR_ZERO;
key.logicOpEnable = false;
key.srcAlpha = VK_BLEND_FACTOR_ZERO;
key.destAlpha = VK_BLEND_FACTOR_ZERO;
key.colorWriteMask = VK_COLOR_COMPONENT_A_BIT;
key.depthCompareOp = VK_COMPARE_OP_LESS; // Inverse of GREATER_EQUAL
key.stencilCompareOp = VK_COMPARE_OP_ALWAYS;
// Invert
key.stencilPassOp = VK_STENCIL_OP_ZERO;
key.stencilFailOp = VK_STENCIL_OP_ZERO;
key.stencilDepthFailOp = VK_STENCIL_OP_KEEP;
// TODO: Need to set in a way that carries over to the next draw..
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
} else {
key.stencilTestEnable = false;
key.stencilCompareOp = VK_COMPARE_OP_ALWAYS;