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

296 lines
11 KiB
C#

// SPDX-License-Identifier: MIT
// Copyright (c) 2026 The Roofer Dev - Beast Roofer Edition. Clean-room integration code.
// Built on Ryujinx (MIT). DLSS, DLAA and NIS are NVIDIA technologies; this is integration code only.
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using System;
using System.Numerics;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
/// <summary>
/// MV++ Phase 2.1 run B v4 (RYUJINX_MVPP_P2B_PROBE=1): empirical transform hunt.
/// v3 verdict: cb9 holds NO world translation at any 16-byte-aligned offset (the broad
/// low-percent hits were the T~0 artifact: a zero translation "projects" whenever the
/// world origin is on screen), and cb10[0x20] is neither an int nor an integral float.
/// Current theory: TOTK's static world geometry is PRE-TRANSFORMED in the vertex data
/// (no model matrix exists -- which is exactly why camera reprojection already nails it),
/// and the transforms that matter for MV++ are the skinning bone palettes (cb5) of the
/// ACTORS. v4 therefore scans DENSELY (every 4-byte float offset, both the contiguous
/// vec3 and the spread row-major x/+4/+8 pattern), across slots {1, 5, 9, 13}, excluding
/// |T| &lt; 0.5 (the origin artifact), and dumps the cb9/cb10 headers as hex (v3 samples
/// looked like handles/addresses, not matrices). CPU-only, zero rendering change.
/// </summary>
static class MvppP2BProbe
{
private static bool _enabled =
Environment.GetEnvironmentVariable("RYUJINX_MVPP_P2B_PROBE") == "1";
private const int LogIntervalMs = 2000;
private const float FrustumMargin = 1.3f;
private const float MinTranslation = 0.5f; // kills the T~0 "origin visible" artifact
private const int SlotCount = 18;
private const int ScanSampleShift = 4; // scan every 16th draw
private const int MaxScanFloats = 5120; // covers cb5's 20 KB palettes
private static readonly int[] _scanSlots = { 1, 5, 9, 13 };
private static long _windowStartMs;
private static int _draws;
private static int _noCam;
private static int _cb9Unbound;
private static int _named;
private static bool _sampleDumped;
private static readonly int[] _slotBound = new int[SlotCount];
private static readonly int[] _scanned = new int[4];
// [slot][conv 0=contiguous vec3, 1=spread x/+4/+8][float offset]
private static readonly int[][][] _hits = CreateHits();
private static int[][][] CreateHits()
{
int[][][] hits = new int[4][][];
for (int s = 0; s < 4; s++)
{
hits[s] = [new int[MaxScanFloats], new int[MaxScanFloats]];
}
return hits;
}
public static void OnDraw(GpuChannel channel)
{
if (!_enabled)
{
return;
}
try
{
OnDrawImpl(channel);
}
catch (Exception e)
{
_enabled = false;
Logger.Warning?.Print(LogClass.Gpu, $"MVPP P2B probe: disabled after unexpected error: {e}");
}
}
private static void OnDrawImpl(GpuChannel channel)
{
if (channel.TextureManager.RenderTargetScale == 1f)
{
return;
}
long now = Environment.TickCount64;
if (_windowStartMs == 0)
{
_windowStartMs = now;
}
else if (now - _windowStartMs >= LogIntervalMs)
{
LogWindow();
_windowStartMs = now;
}
_draws++;
uint mask = channel.BufferManager.GetGraphicsUniformBufferUseMask(0);
for (int s = 0; s < SlotCount; s++)
{
if ((mask & (1u << s)) != 0)
{
_slotBound[s]++;
}
}
if (!DlssCameraState.SnapshotCurrent(out Matrix4x4 vp))
{
_noCam++;
return;
}
_named++;
if ((_named & ((1 << ScanSampleShift) - 1)) != 0)
{
return;
}
for (int s = 0; s < _scanSlots.Length; s++)
{
int slot = _scanSlots[s];
if ((mask & (1u << slot)) == 0)
{
continue;
}
ulong addr = channel.BufferManager.GetGraphicsUniformBufferAddress(0, slot);
int size = channel.BufferManager.GetGraphicsUniformBufferSize(0, slot);
if (addr == 0 || addr == ulong.MaxValue || size < 48)
{
continue;
}
_scanned[s]++;
ReadOnlySpan<float> data = MemoryMarshal.Cast<byte, float>(
channel.MemoryManager.Physical.GetSpan(addr, Math.Min(size, MaxScanFloats * 4)));
int count = data.Length;
int[] contiguous = _hits[s][0];
int[] spread = _hits[s][1];
for (int f = 0; f < count; f++)
{
if (f + 3 <= count)
{
float tx = data[f], ty = data[f + 1], tz = data[f + 2];
if (SaneT(tx, ty, tz) && ProjectsIntoFrustum(in vp, tx, ty, tz))
{
contiguous[f]++;
}
}
if (f + 9 < count)
{
float tx = data[f], ty = data[f + 4], tz = data[f + 8];
if (SaneT(tx, ty, tz) && ProjectsIntoFrustum(in vp, tx, ty, tz))
{
spread[f]++;
}
}
}
}
// One header dump per window: v3's cb9 samples looked like handles/addresses.
// Show cb9 and cb10 heads as raw hex so their true nature is on the record.
if (!_sampleDumped && _named >= 500)
{
_sampleDumped = true;
ulong cb9Addr = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 9);
ulong cb10Addr = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 10);
if (cb9Addr != 0 && cb9Addr != ulong.MaxValue && cb10Addr != 0 && cb10Addr != ulong.MaxValue)
{
ReadOnlySpan<uint> h9 = MemoryMarshal.Cast<byte, uint>(channel.MemoryManager.Physical.GetSpan(cb9Addr, 32));
ReadOnlySpan<uint> h10 = MemoryMarshal.Cast<byte, uint>(channel.MemoryManager.Physical.GetSpan(cb10Addr, 48));
Logger.Info?.Print(LogClass.Gpu,
$"MVPP P2B heads: cb9 [{h9[0]:X8} {h9[1]:X8} {h9[2]:X8} {h9[3]:X8} {h9[4]:X8} {h9[5]:X8} {h9[6]:X8} {h9[7]:X8}] " +
$"cb10 [{h10[0]:X8} {h10[1]:X8} {h10[2]:X8} {h10[3]:X8} {h10[4]:X8} {h10[5]:X8} {h10[6]:X8} {h10[7]:X8} {h10[8]:X8} {h10[9]:X8} {h10[10]:X8} {h10[11]:X8}].");
}
}
}
private static bool SaneT(float x, float y, float z)
{
if (!float.IsFinite(x) || !float.IsFinite(y) || !float.IsFinite(z))
{
return false;
}
float ax = MathF.Abs(x), ay = MathF.Abs(y), az = MathF.Abs(z);
return ax < 1e6f && ay < 1e6f && az < 1e6f &&
(ax >= MinTranslation || ay >= MinTranslation || az >= MinTranslation);
}
/// <summary>Row-major, column-vector convention (clip = M * world), as the whole MV++ chain.</summary>
private static bool ProjectsIntoFrustum(in Matrix4x4 vp, float x, float y, float z)
{
float cw = vp.M41 * x + vp.M42 * y + vp.M43 * z + vp.M44;
if (cw < 1e-3f)
{
return false;
}
float cx = vp.M11 * x + vp.M12 * y + vp.M13 * z + vp.M14;
float cy = vp.M21 * x + vp.M22 * y + vp.M23 * z + vp.M24;
return MathF.Abs(cx / cw) <= FrustumMargin && MathF.Abs(cy / cw) <= FrustumMargin;
}
private static void AppendTopOffsets(System.Text.StringBuilder sb, int[] hits, int scanned)
{
for (int rank = 0; rank < 4; rank++)
{
int best = -1;
int bestHits = 0;
for (int f = 0; f < hits.Length; f++)
{
if (hits[f] > bestHits)
{
bestHits = hits[f];
best = f;
}
}
if (best < 0)
{
break;
}
sb.Append($" 0x{best * 4:X}:{100f * bestHits / scanned:0}%");
hits[best] = 0;
}
}
private static void LogWindow()
{
if (_draws > 0)
{
Logger.Info?.Print(LogClass.Gpu,
$"MVPP P2B probe: {_draws} draws, camera ok {(100f * _named / _draws):0.0}% (noCam {_noCam}, cb9-unbound {_cb9Unbound}).");
System.Text.StringBuilder census = new();
census.Append("MVPP P2B slots(vtx):");
for (int s = 0; s < SlotCount; s++)
{
if (_slotBound[s] > 0)
{
census.Append($" {s}:{100f * _slotBound[s] / _draws:0}%");
}
}
Logger.Info?.Print(LogClass.Gpu, census.ToString());
for (int s = 0; s < _scanSlots.Length; s++)
{
if (_scanned[s] == 0)
{
continue;
}
System.Text.StringBuilder scan = new();
scan.Append($"MVPP P2B scan cb{_scanSlots[s]}(n={_scanned[s]}): vec3");
AppendTopOffsets(scan, _hits[s][0], _scanned[s]);
scan.Append(" | spread");
AppendTopOffsets(scan, _hits[s][1], _scanned[s]);
Logger.Info?.Print(LogClass.Gpu, scan.ToString());
}
}
_draws = 0;
_noCam = 0;
_cb9Unbound = 0;
_named = 0;
_sampleDumped = false;
Array.Clear(_slotBound);
Array.Clear(_scanned);
for (int s = 0; s < 4; s++)
{
Array.Clear(_hits[s][0]);
Array.Clear(_hits[s][1]);
}
}
}
}