Skip to content

Commit b98ae8f

Browse files
committed
GS:MTL: Add workaround for Apple GPU hardware bug
1 parent c10a5c9 commit b98ae8f

5 files changed

Lines changed: 24 additions & 4 deletions

File tree

pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,9 +1073,10 @@ static bool ConvertShaderNotNeeded(ShaderConvert shader)
10731073

10741074
// Init metal stuff
10751075
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]);
1076-
setFnConstantB(m_fn_constants, m_features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
1077-
setFnConstantB(m_fn_constants, m_features.depth_feedback, GSMTLConstantIndex_DEPTH_FEEDBACK);
1078-
setFnConstantB(m_fn_constants, m_dev.features.rov_requires_r32, GSMTLConstantIndex_ROV_NEEDS_R32);
1076+
setFnConstantB(m_fn_constants, m_features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
1077+
setFnConstantB(m_fn_constants, m_features.depth_feedback, GSMTLConstantIndex_DEPTH_FEEDBACK);
1078+
setFnConstantB(m_fn_constants, m_dev.features.rov_requires_r32, GSMTLConstantIndex_ROV_NEEDS_R32);
1079+
setFnConstantB(m_fn_constants, m_dev.features.broken_shader_depth, GSMTLConstantIndex_BROKEN_SHADER_DEPTH);
10791080

10801081
m_draw_sync_fence = MRCTransfer([m_dev.dev newFence]);
10811082
[m_draw_sync_fence setLabel:@"Draw Sync Fence"];
@@ -2350,6 +2351,9 @@ static bool usesStencil(GSHWDrawConfig::DestinationAlphaMode dstalpha)
23502351
if (config.tex && (config.ds == config.tex || config.rt == config.tex))
23512352
EndRenderPass(); // Barrier
23522353

2354+
if (m_dev.features.broken_shader_depth && (config.depth.ztst >= ZTST_GEQUAL || config.depth.zwe))
2355+
config.ps.zfloor = true; // Depth must always go through shader (see tfx vs for comment with details)
2356+
23532357
size_t vertsize = config.nverts * sizeof(*config.verts);
23542358
size_t idxsize = config.vs.UseFixedExpandIndexBuffer() ? 0 : (config.nindices * sizeof(*config.indices));
23552359
Map allocation = Allocate(m_vertex_upload_buf, vertsize + idxsize);

pcsx2/GS/Renderers/Metal/GSMTLDeviceInfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct GSMTLDevice
3737
bool rov : 1;
3838
bool rov_requires_rt : 1;
3939
bool rov_requires_r32 : 1;
40+
bool broken_shader_depth : 1;
4041
MetalVersion shader_version;
4142
int max_texsize;
4243
};

pcsx2/GS/Renderers/Metal/GSMTLDeviceInfo.mm

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ static DetectionResult detectIntelGPU(id<MTLDevice> dev, id<MTLLibrary> lib)
207207
}
208208
else if ([name containsString:@"Apple"])
209209
{
210-
// No special settings
210+
features.broken_shader_depth = true;
211211
}
212212
else
213213
{
@@ -228,6 +228,9 @@ static DetectionResult detectIntelGPU(id<MTLDevice> dev, id<MTLLibrary> lib)
228228
if (char* env = getenv("MTL_ROV_WITH_RT"))
229229
features.rov_requires_rt = env[0] == '1' || env[0] == 'y' || env[0] == 'Y';
230230

231+
if (char* env = getenv("MTL_SHADER_DEPTH_WORKAROUND"))
232+
features.broken_shader_depth = env[0] == '1' || env[0] == 'y' || env[0] == 'Y';
233+
231234
features.max_texsize = GetMaxTextureSize(dev);
232235

233236
this->dev = std::move(dev);

pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ enum GSMTLFnConstants
166166
GSMTLConstantIndex_FRAMEBUFFER_FETCH,
167167
GSMTLConstantIndex_DEPTH_FEEDBACK,
168168
GSMTLConstantIndex_ROV_NEEDS_R32,
169+
GSMTLConstantIndex_BROKEN_SHADER_DEPTH,
169170
GSMTLConstantIndex_FST,
170171
GSMTLConstantIndex_IIP,
171172
GSMTLConstantIndex_VS_POINT_SIZE,

pcsx2/GS/Renderers/Metal/tfx.metal

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ constant uint SHUFFLE_READWRITE = 3;
1414
constant bool HAS_FBFETCH [[function_constant(GSMTLConstantIndex_FRAMEBUFFER_FETCH)]];
1515
constant bool DEPTH_FEEDBACK [[function_constant(GSMTLConstantIndex_DEPTH_FEEDBACK)]];
1616
constant bool ROV_NEEDS_R32 [[function_constant(GSMTLConstantIndex_ROV_NEEDS_R32)]];
17+
constant bool BROKEN_SHADER_DEPTH [[function_constant(GSMTLConstantIndex_BROKEN_SHADER_DEPTH)]];
1718
constant bool FST [[function_constant(GSMTLConstantIndex_FST)]];
1819
constant bool IIP [[function_constant(GSMTLConstantIndex_IIP)]];
1920
constant bool VS_POINT_SIZE [[function_constant(GSMTLConstantIndex_VS_POINT_SIZE)]];
@@ -248,6 +249,14 @@ static MainVSOut vs_main_run(thread const MainVSIn& v, constant GSMTLMainVSUnifo
248249
if (VS_POINT_SIZE)
249250
out.point_size = cb.point_size.x;
250251

252+
// Apple GPUs use slightly different algorithms to calculate the Z they send to the shader vs the Z they use in hardware.
253+
// This breaks a lot of things (the most common is conservative depth rejecting pixels that should have depth equal to current depth but now don't).
254+
// Work around by always routing depth through the shader, and never using hardware depth values.
255+
// To allow us to continue to use [[depth(less)]] optimizations, add a bit in the VS and subtract it off in the FS,
256+
// so that "equal" depth doesn't ever fail a hardware depth test.
257+
if (BROKEN_SHADER_DEPTH)
258+
out.p.z += exp_min32;
259+
251260
return out;
252261
}
253262

@@ -1494,6 +1503,8 @@ struct PSMain
14941503
{
14951504
MainResult out = {};
14961505
float input_z = in.p.z;
1506+
if (BROKEN_SHADER_DEPTH)
1507+
input_z -= 0x1p-32;
14971508
if (PS_ZFLOOR)
14981509
input_z = floor(input_z * 0x1p32) * 0x1p-32;
14991510

0 commit comments

Comments
 (0)