GPU: Clear fragment enable bit on draw

In a few games, depth only draws seem to have random fragment shaders attached to them. In PLA, this isn't too much of an issue as it has rasterize discard enabled, though it still binds resources for an unused fragment stage. However, in Assassin's Creed 2, an alpha testing shader is left bound during the majority of the occlusion culling pass, leading pretty much every fragment to fail and all geometry to be culled out.

This PR clears the fragment stage enable bit after each draw, as the commands set by the guest should re-enable it if it's being used. This means that stale fragment shader bindings are removed during depth-only draws, which can avoid incorrect results if the fragment shader could discard.

All guest GPU drivers seem to religiously set the enable bit for the fragment shader, other stages are better managed (they never stay accidentally enabled). I traced all the registers AC2 wrote when it did the incorrect draws, and this is the only conclusion I could come to.

It's worth testing this in a large number of games. It's entirely possible that this flag is reset in another place, or under specific conditions. OpenGL games are a good one to make sure still work.
This commit is contained in:
riperiperi 2024-02-03 03:51:02 +00:00
parent 6dcb867816
commit 53ee267ba9

View File

@ -319,6 +319,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_context.Renderer.Pipeline.BeginTransformFeedback(_drawState.Topology); _context.Renderer.Pipeline.BeginTransformFeedback(_drawState.Topology);
_prevTfEnable = true; _prevTfEnable = true;
} }
// Unset fragment enable bits for shader, as they need to be reset each draw.
_state.State.ShaderState[(int)ShaderStage.Fragment].Control &= ~(uint)1;
} }
/// <summary> /// <summary>
@ -1398,7 +1401,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
for (int index = 0; index < 6; index++) for (int index = 0; index < 6; index++)
{ {
var shader = _state.State.ShaderState[index]; ref var shader = ref _state.State.ShaderState[index];
if (!shader.UnpackEnable() && index != 1) if (!shader.UnpackEnable() && index != 1)
{ {
continue; continue;