From b26c59460dcba722bcd58c8c41a89cee9d5a008f Mon Sep 17 00:00:00 2001 From: ParantezTech <12572227+par274@users.noreply.github.com> Date: Sun, 11 Oct 2026 06:11:55 +0300 Subject: [PATCH] [Diagnostics] Report the GPU writers of synchronized DCC metadata --- .../Diagnostics/DccWriterTrace.cs | 36 +++++++++++++++++++ .../Gpu/Buffers/GuestBufferCache.cs | 11 ++++++ .../Gpu/Images/GuestImageCache.Metadata.cs | 5 +++ .../Gpu/Rendering/RenderExecutor.Compute.cs | 1 + .../Gpu/Rendering/RenderExecutor.Targets.cs | 6 ++++ 5 files changed, 59 insertions(+) diff --git a/src/SharpEmu.Libs/Diagnostics/DccWriterTrace.cs b/src/SharpEmu.Libs/Diagnostics/DccWriterTrace.cs index ed00daf..322c317 100644 --- a/src/SharpEmu.Libs/Diagnostics/DccWriterTrace.cs +++ b/src/SharpEmu.Libs/Diagnostics/DccWriterTrace.cs @@ -28,6 +28,42 @@ internal static class DccWriterTrace private static readonly HashSet<(ulong Address, ulong Size, ulong Program)> _reported = new(); private static readonly HashSet<(ulong Hash, string Reason)> _refusals = new(); + private static readonly System.Collections.Concurrent.ConcurrentDictionary _writes = new(); + private static readonly HashSet _syncReported = new(); + + [ThreadStatic] + public static string? CurrentKind; + + public static void RecordWrite(ulong address, ulong size) + { + if (DccEnabled) + { + _writes[address] = (size, CurrentProgram, CurrentKind ?? "?"); + } + } + + public static void ReportSync(ulong metadataAddress, ulong size) + { + if (!DccEnabled) + { + return; + } + + lock (_syncReported) + { + if (_syncReported.Count >= 64 || !_syncReported.Add(metadataAddress)) + { + return; + } + } + + var writers = _writes + .Where(pair => pair.Key < metadataAddress + size && metadataAddress < pair.Key + pair.Value.Size) + .Take(8) + .Select(pair => $"0x{pair.Key:X}+0x{pair.Value.Size:X}:{pair.Value.Kind}:0x{pair.Value.Program:X16}"); + Console.Error.WriteLine( + $"[GPU][DCC_SYNC_WRITER] metadata=0x{metadataAddress:X16} size=0x{size:X} writers={string.Join(" ", writers)}"); + } private static ulong ParseAddress(string? value) { diff --git a/src/SharpEmu.Libs/Gpu/Buffers/GuestBufferCache.cs b/src/SharpEmu.Libs/Gpu/Buffers/GuestBufferCache.cs index b4a8ed6..1fc7ad5 100644 --- a/src/SharpEmu.Libs/Gpu/Buffers/GuestBufferCache.cs +++ b/src/SharpEmu.Libs/Gpu/Buffers/GuestBufferCache.cs @@ -244,6 +244,7 @@ public sealed unsafe class GuestBufferCache : IGuestBufferStore, IDisposable if (Diagnostics.DccWriterTrace.Enabled) { Diagnostics.DccWriterTrace.Record(RequireImageCache(), guestAddress, size); + Diagnostics.DccWriterTrace.RecordWrite(guestAddress, size); } if (!_gpuModifiedRanges.Contains(guestAddress, size)) @@ -412,6 +413,11 @@ public sealed unsafe class GuestBufferCache : IGuestBufferStore, IDisposable var (destination, destinationOffset) = ObtainBuffer(guestAddress, size, true, true, bufferIdentifier); destination.Fill(destinationOffset, size, value); if (images.OverlapsDccMetadata(guestAddress, size) && TryWriteFillToBacking(guestAddress, size, value)) + if (Diagnostics.DccWriterTrace.Enabled) + { + Diagnostics.DccWriterTrace.CurrentKind = "fill"; + } + { _gpuModifiedRanges.Remove(guestAddress, size); Interlocked.Increment(ref _gpuModifiedVersion); @@ -642,6 +648,11 @@ public sealed unsafe class GuestBufferCache : IGuestBufferStore, IDisposable var (dst, dstOffset) = dstMemory ? ObtainBuffer(dstVaddr, size, true, true, dstId) : (_gds, dstVaddr); if (ReferenceEquals(src, dst) && srcOffset < dstOffset + size && dstOffset < srcOffset + size) { + if (Diagnostics.DccWriterTrace.Enabled) + { + Diagnostics.DccWriterTrace.CurrentKind = $"copy_from_0x{srcVaddr:X}"; + } + throw SubmissionScheduler.Fatal("The resolved Vulkan copy ranges overlap."); } diff --git a/src/SharpEmu.Libs/Gpu/Images/GuestImageCache.Metadata.cs b/src/SharpEmu.Libs/Gpu/Images/GuestImageCache.Metadata.cs index 660acfd..c05998f 100644 --- a/src/SharpEmu.Libs/Gpu/Images/GuestImageCache.Metadata.cs +++ b/src/SharpEmu.Libs/Gpu/Images/GuestImageCache.Metadata.cs @@ -436,6 +436,11 @@ public sealed partial class GuestImageCache if (IsValidRange(address, size) && _bufferCache.HasGpuDirtyBytes(address, size)) { Interlocked.Increment(ref _dccSyncs); + if (Diagnostics.DccWriterTrace.Enabled) + { + Diagnostics.DccWriterTrace.ReportSync(address, size); + } + _ = _bufferCache.TrySynchronizeCpuRead(address, size); } } diff --git a/src/SharpEmu.Libs/Gpu/Rendering/RenderExecutor.Compute.cs b/src/SharpEmu.Libs/Gpu/Rendering/RenderExecutor.Compute.cs index ac3a7fd..9eafa94 100644 --- a/src/SharpEmu.Libs/Gpu/Rendering/RenderExecutor.Compute.cs +++ b/src/SharpEmu.Libs/Gpu/Rendering/RenderExecutor.Compute.cs @@ -72,6 +72,7 @@ public sealed partial class RenderExecutor if (Diagnostics.DccWriterTrace.Enabled) { Diagnostics.DccWriterTrace.CurrentProgram = program.Hash; + Diagnostics.DccWriterTrace.CurrentKind = "compute"; } if (RenderTrace.Enabled) diff --git a/src/SharpEmu.Libs/Gpu/Rendering/RenderExecutor.Targets.cs b/src/SharpEmu.Libs/Gpu/Rendering/RenderExecutor.Targets.cs index 38fa148..2a2afcd 100644 --- a/src/SharpEmu.Libs/Gpu/Rendering/RenderExecutor.Targets.cs +++ b/src/SharpEmu.Libs/Gpu/Rendering/RenderExecutor.Targets.cs @@ -17,6 +17,12 @@ public sealed partial class RenderExecutor private bool TryResolveDrawTargets(RegisterBanks banks, in DrawCall draw, ref DrawState state) { using var profileScope = RenderPhaseProfile.MeasureDetail(RenderPhaseProfile.Phase.DrawTargetResolution); + if (Diagnostics.DccWriterTrace.Enabled) + { + Diagnostics.DccWriterTrace.CurrentKind = "draw"; + Diagnostics.DccWriterTrace.CurrentProgram = 0; + } + var context = banks.Context; if (TryResolveMultisampleColor(context)) {