[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
{
private const int MaxReports = 64;
private const int MaxReports = 256;
private const int MaxFrames = 12;
private static readonly bool DccEnabled =
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]
public static ulong CurrentProgram;
@ -43,7 +47,7 @@ internal static class DccWriterTrace
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))) ||
!_reported.Add((address, size, CurrentProgram)))
{

View File

@ -17,6 +17,30 @@ internal static class GpuReadTrace
public static string? CurrentStage;
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)
{

View File

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