[Diagnostics] Summarize synchronized table reads and trace several writer addresses

SHARPEMU_TRACE_GPU_READS=1 now also reports, every five seconds, the guest
words resource planning read through a GPU synchronization: read count, how
often the value changed, and the first and last value. SHARPEMU_TRACE_WRITER_ADDRESS
accepts a comma-separated list.
This commit is contained in:
ParantezTech 2026-10-04 16:32:25 +03:00
parent ca102a67c7
commit 5f26576d6b
3 changed files with 40 additions and 4 deletions

View File

@ -9,15 +9,19 @@ namespace SharpEmu.Libs.Diagnostics;
internal static class DccWriterTrace internal static class DccWriterTrace
{ {
private const int MaxReports = 64; private const int MaxReports = 256;
private const int MaxFrames = 12; private const int MaxFrames = 12;
private static readonly bool DccEnabled = private static readonly bool DccEnabled =
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_DCC_WRITERS") == "1"; Environment.GetEnvironmentVariable("SHARPEMU_TRACE_DCC_WRITERS") == "1";
private static readonly ulong TargetAddress = ParseAddress(Environment.GetEnvironmentVariable("SHARPEMU_TRACE_WRITER_ADDRESS")); private static readonly ulong[] TargetAddresses = (Environment.GetEnvironmentVariable("SHARPEMU_TRACE_WRITER_ADDRESS") ?? string.Empty)
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Select(ParseAddress)
.Where(address => address != 0)
.ToArray();
public static readonly bool Enabled = DccEnabled || TargetAddress != 0; public static readonly bool Enabled = DccEnabled || TargetAddresses.Length != 0;
[ThreadStatic] [ThreadStatic]
public static ulong CurrentProgram; public static ulong CurrentProgram;
@ -43,7 +47,7 @@ internal static class DccWriterTrace
public static void Record(IGuestImageCache images, ulong address, ulong size) public static void Record(IGuestImageCache images, ulong address, ulong size)
{ {
var targeted = TargetAddress != 0 && TargetAddress >= address && TargetAddress - address < size; var targeted = TargetAddresses.Any(target => target >= address && target - address < size);
if (_reported.Count >= MaxReports || (!targeted && !(DccEnabled && images.OverlapsDccMetadata(address, size))) || if (_reported.Count >= MaxReports || (!targeted && !(DccEnabled && images.OverlapsDccMetadata(address, size))) ||
!_reported.Add((address, size, CurrentProgram))) !_reported.Add((address, size, CurrentProgram)))
{ {

View File

@ -17,6 +17,30 @@ internal static class GpuReadTrace
public static string? CurrentStage; public static string? CurrentStage;
private static readonly HashSet<(ulong Shader, ulong Page, bool Table)> _reported = new(); private static readonly HashSet<(ulong Shader, ulong Page, bool Table)> _reported = new();
private static readonly Dictionary<ulong, (long Reads, long Changes, uint Last, uint First, ulong Shader)> _values = new();
private static long _nextValueReport = System.Diagnostics.Stopwatch.GetTimestamp();
public static void RecordValue(ulong address, uint word)
{
lock (_values)
{
_values[address] = _values.TryGetValue(address, out var entry)
? (entry.Reads + 1, entry.Changes + (entry.Last != word ? 1 : 0), word, entry.First, entry.Shader)
: (1, 0, word, word, CurrentShader);
var now = System.Diagnostics.Stopwatch.GetTimestamp();
if (now < _nextValueReport)
{
return;
}
_nextValueReport = now + System.Diagnostics.Stopwatch.Frequency * 5;
foreach (var (key, value) in _values.OrderByDescending(pair => pair.Value.Reads).Take(16))
{
Console.Error.WriteLine(
$"[GPU][SYNC_READ_VALUE] address=0x{key:X16} reads={value.Reads} changes={value.Changes} first=0x{value.First:X8} last=0x{value.Last:X8} shader=0x{value.Shader:X16}");
}
}
}
public static void Record(ulong address, bool table) public static void Record(ulong address, bool table)
{ {

View File

@ -100,6 +100,7 @@ internal static unsafe partial class VulkanVideoPresenter
{ {
using var profile = ResourceMaterializationProfile.Measure(ResourceMaterializationProfile.Phase.GuestRead); using var profile = ResourceMaterializationProfile.Measure(ResourceMaterializationProfile.Phase.GuestRead);
word = 0; word = 0;
var synchronized = false;
if (IsCleanReadPage(address, sizeof(uint))) if (IsCleanReadPage(address, sizeof(uint)))
{ {
if (TryGetAliasPointer(address, sizeof(uint), out var alias)) if (TryGetAliasPointer(address, sizeof(uint), out var alias))
@ -132,6 +133,8 @@ internal static unsafe partial class VulkanVideoPresenter
{ {
return false; return false;
} }
synchronized = true;
} }
else else
{ {
@ -146,6 +149,11 @@ internal static unsafe partial class VulkanVideoPresenter
} }
word = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(bytes); word = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(bytes);
if (Diagnostics.GpuReadTrace.Enabled && synchronized)
{
Diagnostics.GpuReadTrace.RecordValue(address, word);
}
return true; return true;
} }