mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-08 10:45:52 +08:00
AGC: honour DCC fast clears instead of drawing the clear quad (#738)
On GFX10 a colour clear is not a packet. The driver programs CB_COLORn_CLEAR_WORD0/1 and draws a covering quad which the colour block turns into DCC clear codes, discarding whatever the pixel shader exported. We executed that quad as an ordinary draw, so the shaded output landed in the surface instead of a clear. That alone would be a wrong-pixels bug, but the blend those quads use makes it compound. Every draw into the target blends src=ONE, dst=ONE_MINUS_SRC_ALPHA, so alpha follows a <- a_src + a_dst*(1 - a_src), whose fixed point is 1. A guest colour attachment is cleared once on first use and loaded on every pass after, so nothing ever resets it and the channel climbs until it saturates. Where such a surface is a compositing layer, the final image is ui.rgb + scene.rgb*(1 - ui.a) and a saturated alpha multiplies the scene away entirely - the scene renders correctly the whole time and is then masked to black. Recognise the clear and perform it: the attachment is reset and the quad is dropped, which reproduces the observable effect without modelling DCC block state. The reset drops the image's Initialized flag so the next render pass clears via AttachmentLoadOp.Clear, rather than enqueuing a CmdClearColorImage - the latter lands outside the following render pass, and a target cleared that way was still observed reading back its previous contents. Restricted to clear-to-zero: the reset clears to zero, so a nonzero CLEAR_WORD would be cleared to the wrong colour and is left to be drawn. Zero is zero under every encoding the register pair can carry, so the test needs no format handling. The clip-space span test is load-bearing rather than defensive. Fills sharing the vertex count, topology and blend outnumber the clears by two orders of magnitude and sit well outside the frame; treating those as clears erases the UI and blanks video surfaces.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
using Silk.NET.Core;
|
||||
using Silk.NET.Core.Native;
|
||||
using System.Collections.Concurrent;
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Agc;
|
||||
using SharpEmu.Libs.Media;
|
||||
@@ -1259,6 +1260,25 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<ulong, byte> _pendingGuestColorClears = new();
|
||||
|
||||
/// <summary>
|
||||
/// Clear a guest colour target to zero at its next render pass.
|
||||
///
|
||||
/// Deliberately not <see cref="SubmitOffscreenColorClear"/>: that enqueues
|
||||
/// a CmdClearColorImage which lands outside the render pass that follows
|
||||
/// it, so a target cleared this way was still observed reading back its
|
||||
/// previous contents. Dropping <c>Initialized</c> makes the render pass
|
||||
/// itself clear via <see cref="AttachmentLoadOp.Clear"/>.
|
||||
/// </summary>
|
||||
internal static void RequestGuestColorClear(ulong address)
|
||||
{
|
||||
if (address != 0)
|
||||
{
|
||||
_pendingGuestColorClears[address] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a solid color clear to offscreen guest render targets without a
|
||||
/// graphics pipeline. Used for empty-SRT procedural clear draws that
|
||||
@@ -12412,6 +12432,17 @@ internal static unsafe class VulkanVideoPresenter
|
||||
// format could later be replayed inside a render pass of the
|
||||
// other identity.
|
||||
formats[index] = targets[index].Format;
|
||||
|
||||
// Guest colour attachments load their previous contents on
|
||||
// every pass after the first, so nothing resets one until the
|
||||
// guest clears it. Consume a pending clear here, before the
|
||||
// render pass is built, so the pass uses LoadOp.Clear.
|
||||
if (work.Targets[index].Address != 0 &&
|
||||
_pendingGuestColorClears.TryRemove(work.Targets[index].Address, out _))
|
||||
{
|
||||
targets[index].Initialized = false;
|
||||
}
|
||||
|
||||
if (work.Targets[index].Address != 0 &&
|
||||
TakeGuestImageInitialData(work.Targets[index].Address) is { } initialData &&
|
||||
!targets[index].Initialized &&
|
||||
|
||||
Reference in New Issue
Block a user