GPU: Avoid clears for non-simple depth values.

Some drivers don't round depth the same way for a clear vs for drawing,
which can cause mismatches.  We only do this if we see equals-style depth
comparison funcs used in drawing.
This commit is contained in:
Unknown W. Brackets 2022-11-07 20:07:57 -08:00
parent 818849c4b9
commit 3333f2a5aa
9 changed files with 38 additions and 0 deletions

View file

@ -145,11 +145,28 @@ protected:
return 1;
}
inline void UpdateEverUsedEqualDepth(GEComparison comp) {
switch (comp) {
case GE_COMP_EQUAL:
case GE_COMP_NOTEQUAL:
case GE_COMP_LEQUAL:
case GE_COMP_GEQUAL:
everUsedEqualDepth_ = true;
break;
default:
break;
}
}
bool useHWTransform_ = false;
bool useHWTessellation_ = false;
// Used to prevent unnecessary flushing in softgpu.
bool flushOnParams_ = true;
// Set once a equal depth test is encountered.
bool everUsedEqualDepth_ = false;
// Vertex collector buffers
u8 *decoded = nullptr;
u16 *decIndex = nullptr;

View file

@ -643,6 +643,10 @@ rotateVBO:
swTransform.BuildDrawingParams(prim, indexGen.VertexCount(), dec_->VertexType(), inds, maxIndex, &result);
if (result.setSafeSize)
framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight);
// Non-zero depth clears are unusual, but some drivers don't match drawn depth values to cleared values.
// Games sometimes expect exact matches (see #12626, for example) for equal comparisons.
if (result.action == SW_CLEAR && everUsedEqualDepth_ && gstate.isClearModeDepthMask() && result.depth > 0.0f && result.depth < 1.0f)
result.action = SW_DRAW_PRIMITIVES;
ApplyDrawStateLate(result.setStencil, result.stencilValue);

View file

@ -274,6 +274,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
keys_.depthStencil.depthTestEnable = true;
keys_.depthStencil.depthCompareOp = compareOps[gstate.getDepthTestFunction()];
keys_.depthStencil.depthWriteEnable = gstate.isDepthWriteEnabled();
UpdateEverUsedEqualDepth(gstate.getDepthTestFunction());
} else {
keys_.depthStencil.depthTestEnable = false;
keys_.depthStencil.depthWriteEnable = false;

View file

@ -611,6 +611,10 @@ rotateVBO:
swTransform.BuildDrawingParams(prim, indexGen.VertexCount(), dec_->VertexType(), inds, maxIndex, &result);
if (result.setSafeSize)
framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight);
// Non-zero depth clears are unusual, but some drivers don't match drawn depth values to cleared values.
// Games sometimes expect exact matches (see #12626, for example) for equal comparisons.
if (result.action == SW_CLEAR && everUsedEqualDepth_ && gstate.isClearModeDepthMask() && result.depth > 0.0f && result.depth < 1.0f)
result.action = SW_DRAW_PRIMITIVES;
ApplyDrawStateLate();

View file

@ -232,6 +232,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
dxstate.depthTest.enable();
dxstate.depthFunc.set(ztests[gstate.getDepthTestFunction()]);
dxstate.depthWrite.set(gstate.isDepthWriteEnabled());
UpdateEverUsedEqualDepth(gstate.getDepthTestFunction());
} else {
dxstate.depthTest.disable();
}

View file

@ -404,6 +404,10 @@ void DrawEngineGLES::DoFlush() {
swTransform.BuildDrawingParams(prim, vertexCount, dec_->VertexType(), inds, maxIndex, &result);
if (result.setSafeSize)
framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight);
// Non-zero depth clears are unusual, but some drivers don't match drawn depth values to cleared values.
// Games sometimes expect exact matches (see #12626, for example) for equal comparisons.
if (result.action == SW_CLEAR && everUsedEqualDepth_ && gstate.isClearModeDepthMask() && result.depth > 0.0f && result.depth < 1.0f)
result.action = SW_DRAW_PRIMITIVES;
ApplyDrawStateLate(result.setStencil, result.stencilValue);

View file

@ -254,6 +254,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
} else {
// Depth Test
renderManager->SetDepth(gstate.isDepthTestEnabled(), gstate.isDepthWriteEnabled(), compareOps[gstate.getDepthTestFunction()]);
if (gstate.isDepthTestEnabled())
UpdateEverUsedEqualDepth(gstate.getDepthTestFunction());
// Stencil Test
if (stencilState.enabled) {

View file

@ -899,6 +899,10 @@ void DrawEngineVulkan::DoFlush() {
if (result.setSafeSize)
framebufferManager_->SetSafeSize(result.safeWidth, result.safeHeight);
// Non-zero depth clears are unusual, but some drivers don't match drawn depth values to cleared values.
// Games sometimes expect exact matches (see #12626, for example) for equal comparisons.
if (result.action == SW_CLEAR && everUsedEqualDepth_ && gstate.isClearModeDepthMask() && result.depth > 0.0f && result.depth < 1.0f)
result.action = SW_DRAW_PRIMITIVES;
// Only here, where we know whether to clear or to draw primitives, should we actually set the current framebuffer! Because that gives use the opportunity
// to use a "pre-clear" render pass, for high efficiency on tilers.

View file

@ -278,6 +278,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
key.depthTestEnable = true;
key.depthCompareOp = compareOps[gstate.getDepthTestFunction()];
key.depthWriteEnable = gstate.isDepthWriteEnabled();
UpdateEverUsedEqualDepth(gstate.getDepthTestFunction());
} else {
key.depthTestEnable = false;
key.depthWriteEnable = false;