Files
the-beast-roofer-edition/src/Ryujinx.Graphics.Gpu/Engine/Threed/MvppPreSyncProbe.cs
T

186 lines
6.3 KiB
C#

using Ryujinx.Common.Logging;
using System;
using System.IO;
namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
static class MvppPreSyncProbe
{
private static bool _enabled =
Environment.GetEnvironmentVariable("RYUJINX_PRESYNC") == "1";
private static readonly int _vkey =
int.TryParse(Environment.GetEnvironmentVariable("RYUJINX_PRESYNC_VKEY"), out int vk) && vk > 0 ? vk : 0x79;
private static readonly int _maxShots =
int.TryParse(Environment.GetEnvironmentVariable("RYUJINX_PRESYNC_MAX"), out int mx) && mx > 0 ? mx : 4;
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);
private static bool _announced;
private static int _shot;
private static long _nextMs;
private static byte[] _before;
private static bool _armedThisFrame;
private static int _guestShots;
private static long _nextGuestMs;
private static bool KeyHeld()
{
if (!OperatingSystem.IsWindows())
{
return false;
}
try
{
return (GetAsyncKeyState(_vkey) & 0x8000) != 0;
}
catch
{
return false;
}
}
public static void DumpGuest(Image.Texture texture)
{
if (!_enabled || texture == null)
{
return;
}
try
{
long now = Environment.TickCount64;
if (_guestShots >= _maxShots || now < _nextGuestMs || !KeyHeld())
{
return;
}
ReadOnlySpan<byte> guest = texture.PhysicalMemory.GetSpan(texture.Range);
Image.TextureInfo gi = texture.Info;
string dir = Path.Combine("presync", $"guest{_guestShots:D2}");
Directory.CreateDirectory(dir);
File.WriteAllBytes(
Path.Combine(dir, $"MEMOIRE_{gi.Width}x{gi.Height}_lin{gi.IsLinear}_gobY{gi.GobBlocksInY}_stride{gi.Stride}_{gi.FormatInfo.Format}.bin"),
guest.ToArray());
using GAL.PinnedSpan<byte> host = texture.HostTexture.GetData();
File.WriteAllBytes(
Path.Combine(dir, $"HOTE_{gi.Width}x{gi.Height}_{gi.FormatInfo.Format}.bin"),
host.Get().ToArray());
Logger.Info?.Print(LogClass.Gpu,
$"MVPP PRESYNC GUEST shot {_guestShots}: memoire invitee {guest.Length} octets + texture hote, " +
$"{gi.Width}x{gi.Height} lin={gi.IsLinear} gobY={gi.GobBlocksInY} stride={gi.Stride} {gi.FormatInfo.Format} -> {dir}");
_guestShots++;
_nextGuestMs = now + 2000;
}
catch (Exception e)
{
_enabled = false;
Logger.Warning?.Print(LogClass.Gpu, $"MVPP PRESYNC GUEST: desactive apres erreur: {e.Message}");
}
}
public static void Before(Image.Texture texture)
{
if (!_enabled || texture?.HostTexture == null)
{
return;
}
try
{
if (!_announced)
{
_announced = true;
Logger.Info?.Print(LogClass.Gpu,
"MVPP PRESYNC: ON -- APPUIE SUR F10 quand tu VOIS l'artefact. Capture l'image presentee " +
"AVANT et APRES la resynchronisation, pour voir si c'est ELLE qui la detruit.");
}
_armedThisFrame = false;
if (_shot >= _maxShots)
{
return;
}
long now = Environment.TickCount64;
if (now < _nextMs || !KeyHeld())
{
return;
}
using GAL.PinnedSpan<byte> data = texture.HostTexture.GetData();
_before = data.Get().ToArray();
_armedThisFrame = true;
}
catch (Exception e)
{
_enabled = false;
Logger.Warning?.Print(LogClass.Gpu, $"MVPP PRESYNC: desactive apres erreur (before): {e.Message}");
}
}
public static void After(Image.Texture texture)
{
if (!_enabled || !_armedThisFrame || _before == null || texture?.HostTexture == null)
{
return;
}
_armedThisFrame = false;
try
{
using GAL.PinnedSpan<byte> data = texture.HostTexture.GetData();
byte[] after = data.Get().ToArray();
bool identical = _before.Length == after.Length && _before.AsSpan().SequenceEqual(after);
long diffBytes = 0;
if (!identical && _before.Length == after.Length)
{
for (int i = 0; i < after.Length; i++)
{
if (_before[i] != after[i])
{
diffBytes++;
}
}
}
string dir = Path.Combine("presync", $"shot{_shot:D2}");
Directory.CreateDirectory(dir);
string tag = $"{texture.Info.Width}x{texture.Info.Height}_{texture.Info.FormatInfo.Format}";
File.WriteAllBytes(Path.Combine(dir, $"AVANT_{tag}.bin"), _before);
File.WriteAllBytes(Path.Combine(dir, $"APRES_{tag}.bin"), after);
double pct = after.Length > 0 ? 100.0 * diffBytes / after.Length : 0;
Logger.Info?.Print(LogClass.Gpu,
$"MVPP PRESYNC shot {_shot}: {tag} -- {(identical ? "IDENTIQUES (la resync ne touche rien)" : $"DIFFERENTS, {pct:F1}% des octets changes par la resync")} -> {dir}");
_shot++;
_nextMs = Environment.TickCount64 + 2000;
_before = null;
}
catch (Exception e)
{
_enabled = false;
Logger.Warning?.Print(LogClass.Gpu, $"MVPP PRESYNC: desactive apres erreur (after): {e.Message}");
}
}
}
}