Skip to content
Permalink
Browse files

Workaround ARM Mali depth hardware bug. Fixes #11937

When triangles coincide with the Z=1 plane in specific ways, triggered
by Burnout Legends' sky for example, the depth buffer gets corrupted.

This is worked around here by slightly rescaling Z. This type of
workaround is recommended by ARM driver engineers.

Ugly but what can you do when the hardware is bugged. I've done quick
tests on a number of games with no issues.
  • Loading branch information...
hrydgard committed Sep 4, 2019
1 parent 3e43c4f commit 0462c01228bc9a1f547ce6d6c97aabf62767e5eb
@@ -363,3 +363,6 @@ std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props);

// Simple heuristic.
bool IsHashMaliDriverVersion(const VkPhysicalDeviceProperties &props);

// For the ARM Mali depth scale hack.
#define DEPTH_SCALE_HACK_VALUE 0.9999f
@@ -495,6 +495,7 @@ enum {
GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT = FLAG_BIT(26),
GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT = FLAG_BIT(27),
GPU_SUPPORTS_OES_TEXTURE_NPOT = FLAG_BIT(28),
GPU_NEEDS_DEPTH_SCALE_HACK = FLAG_BIT(29),
GPU_PREFER_CPU_DOWNLOAD = FLAG_BIT(30),
GPU_PREFER_REVERSE_COLOR_ORDER = FLAG_BIT(31),
};
@@ -962,6 +962,10 @@ void DrawEngineVulkan::DoFlush() {

// We let the framebuffer manager handle the clear. It can use renderpasses to optimize on tilers.
// If non-buffered though, it'll just do a plain clear.
float depth = result.depth;
if (gstate_c.Supports(GPU_NEEDS_DEPTH_SCALE_HACK)) {
depth *= DEPTH_SCALE_HACK_VALUE;
}
framebufferManager_->NotifyClear(gstate.isClearModeColorMask(), gstate.isClearModeAlphaMask(), gstate.isClearModeDepthMask(), result.color, result.depth);

int scissorX1 = gstate.getScissorX1();
@@ -970,7 +974,7 @@ void DrawEngineVulkan::DoFlush() {
int scissorY2 = gstate.getScissorY2() + 1;
framebufferManager_->SetSafeSize(scissorX2, scissorY2);

if ((gstate_c.featureFlags & GPU_USE_CLEAR_RAM_HACK) && gstate.isClearModeColorMask() && (gstate.isClearModeAlphaMask() || gstate.FrameBufFormat() == GE_FORMAT_565)) {
if (gstate_c.Supports(GPU_USE_CLEAR_RAM_HACK) && gstate.isClearModeColorMask() && (gstate.isClearModeAlphaMask() || gstate.FrameBufFormat() == GE_FORMAT_565)) {
framebufferManager_->ApplyClearToMemory(scissorX1, scissorY1, scissorX2, scissorY2, result.color);
}
}
@@ -205,11 +205,16 @@ void GPU_Vulkan::CheckGPUFeatures() {
if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth || driverTooOld) {
features |= GPU_SUPPORTS_ACCURATE_DEPTH;
}
// These GPUs (up to some certain hardware version?) has a bug where draws that touch the upper range of Z
// depth test incorrectly. This is easily worked around by simply scaling Z down a tiny bit, with a small risk
// for artifacts - haven't seen any though.
features |= GPU_NEEDS_DEPTH_SCALE_HACK;
break;
}
default:
if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth)
if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth) {
features |= GPU_SUPPORTS_ACCURATE_DEPTH;
}
break;
}

@@ -175,8 +175,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) {
if (!useHWTransform && doTextureTransform && !isModeThrough) {
WRITE(p, "layout (location = %d) in vec3 texcoord;\n", (int)PspAttributeLocation::TEXCOORD);
texcoordInVec3 = true;
}
else
} else
WRITE(p, "layout (location = %d) in vec2 texcoord;\n", (int)PspAttributeLocation::TEXCOORD);
}
if (hasColor) {
@@ -637,6 +636,10 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) {
}
WRITE(p, " gl_Position = outPos;\n");

if (gstate_c.Supports(GPU_NEEDS_DEPTH_SCALE_HACK)) {
WRITE(p, " gl_Position.z *= %f;\n", DEPTH_SCALE_HACK_VALUE);
}

WRITE(p, "}\n");
return true;
}
@@ -134,6 +134,7 @@ class VulkanRenderManager {
// TODO: This should be fixed at the source.
data.viewport.vp.maxDepth = clamp_value(vp.maxDepth, 0.0f, 1.0f);
data.viewport.vp.minDepth = clamp_value(vp.minDepth, 0.0f, 1.0f);

curRenderStep_->commands.push_back(data);
}

0 comments on commit 0462c01

Please sign in to comment.
You can’t perform that action at this time.