6634d3ef87
Removes a bug introduced in v1.2.3: the DLSS clip-space jitter block was emitted into every vertex shader and read the Position output before writing it. Reading an unwritten output is undefined, so vertex positions could come out as garbage - whole 3D passes landing nowhere (the green screen reported on GYLT, Ghostbusters, Blair Witch and others) and the black block artifacts in Xenoblade Chronicles 2's motion-blur pass, which had been mistaken for a long-standing emulation defect and worked around by disabling the effect. With jitter off - how this fork ships - the vertex epilogue is now identical to the base emulator again, and the motion blur no longer needs to be disabled. CodeGenVersion bumped so existing shader caches are rebuilt. Also adds a DLSS sharpening slider (0-100, off by default) and an experimental advanced motion stack checkbox (off by default).
205 lines
9.4 KiB
C#
205 lines
9.4 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 System;
|
|
|
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|
{
|
|
/// <summary>
|
|
/// ⛔⛔ MORT PAR LA MESURE, 29/07 AU SOIR -- NE PAS RE-ARMER, NE PAS RE-REGLER. Sonde `HDRTRY` :
|
|
/// la passe de scene HDR existe dans **98,9 %** des images 3D, mais la camera n'y est **lisible
|
|
/// que dans 36,5 %** des cas. N'autoriser QUE cette passe ferait donc tomber la publication de
|
|
/// 83 % a ~37 % : la camera serait AFFAMEE, et une camera manquee fait ecrire zero sur toute
|
|
/// l'image. Ce n'est pas un probleme de reglage du filet -- le filet mal dimensionne (voir plus
|
|
/// bas) n'etait que le symptome. Le remplacant est <see cref="MvppLdrSkip"/>, qui ECARTE la pire
|
|
/// passe au lieu d'en ELIRE une. Ce fichier est conserve pour la trace, et parce que
|
|
/// <see cref="IsSceneColorPass"/> sert encore a la sonde.
|
|
///
|
|
/// [SCENEPASS 29/07 SOIR] Capturer la camera sur LA PASSE DE LA SCENE, et non sur la premiere
|
|
/// passe 3D venue (RYUJINX_MVPP_SCENEPASS=1, coupe par defaut).
|
|
///
|
|
/// POURQUOI, ET SUR QUELLE MESURE. <see cref="MvppCameraCapture.OnDraw"/> capture au PREMIER
|
|
/// dessin de l'image qui passe le pre-filtre, puis se tait. Ce pre-filtre reconnait "une passe
|
|
/// 3D", pas "LA passe de la scene". Recensement CTXPROBE du 29/07 au soir, run etalon de 3 min,
|
|
/// 3 633 lectures et 177 intruses -- du GROS volume, pas trois evenements tires au sort :
|
|
///
|
|
/// cible couleur branchee a la lecture lectures intruses taux
|
|
/// R8G8B8A8Unorm (8 bits, LDR) 1 340 119 8,9 % <- 67 % des intruses
|
|
/// aucune (passe profondeur seule) 2 044 52 2,5 %
|
|
/// R11G11B10Float (couleur HDR de scene) 230 3 1,3 % <- la vraie passe
|
|
/// R32Float 19 3 15,8 %
|
|
///
|
|
/// La passe 8 bits donne SEPT FOIS plus d'intruses que la couleur HDR et fournit deux tiers du
|
|
/// total. Le discriminant que le dossier croyait inexistant n'est pas dans la MATRICE (fermé le
|
|
/// 27/07 : vue-projection structurellement parfaite, rien ne l'identifie) -- il est dans la
|
|
/// PASSE. Et il y a de quoi choisir : 125 passes qualifiantes par image en moyenne, dont ~87
|
|
/// sur la couleur HDR.
|
|
///
|
|
/// LE CRITERE EST GENERIQUE -- forme de pipeline, rien d'autre : cible couleur attachee, NON
|
|
/// CARREE (une cible carree est un atlas d'ombres), format flottant HDR. C'est mot pour mot
|
|
/// celui que <see cref="MvppColorSnapshot"/> utilise deja pour epingler la couleur de scene.
|
|
/// Aucune adresse, aucun hash, aucune resolution, aucun nom de jeu.
|
|
///
|
|
/// ⛔ CE QUE CE FICHIER NE PROMET PAS. Attendu : 4,9 % d'intruses -> 1,3 %, soit environ 73 %
|
|
/// de moins. PAS zero : la passe de scene elle-meme en porte 3 sur 230. C'est une attenuation,
|
|
/// obtenue a la source au lieu d'un filtre pose apres coup -- ce n'est pas une execution.
|
|
///
|
|
/// ⚠️ LE RISQUE EST LA PUBLICATION, PAS L'INTRUSE, et c'est la borne INFERIEURE qui protege
|
|
/// (lecon du 29/07 : pour un composant dont le role est de refuser, prouver "il ne refuse
|
|
/// jamais plus" est la moitie inutile de la question). Une camera manquee fait ecrire ZERO sur
|
|
/// toute l'image. Donc la restriction s'auto-desarme, en deux etages :
|
|
/// 1. RATTRAPAGE -- deux images d'affilee avec des passes qualifiantes mais AUCUNE capture,
|
|
/// et la restriction se leve pour 300 images (comportement d'avant, a l'identique). Perte
|
|
/// bornee : 2 images sur 300, soit 0,7 %.
|
|
/// 2. DESARMEMENT -- cinq rattrapages, et la restriction se coupe pour la session, avec une
|
|
/// ligne de journal explicite. Si la passe HDR n'est pas fiable sur ce jeu, on le DIT au
|
|
/// lieu de saigner 2 images sur 3.
|
|
/// Le taux de publication se lit dans l'instrument qui existe deja ("MVPP capture window:
|
|
/// enq/pub") : mesure de reference du 29/07 au soir = 6 761 / 8 127 = 83,2 %. En dessous, le
|
|
/// correctif est mauvais quel que soit son gain sur les intruses.
|
|
///
|
|
/// Les compteurs sont approximatifs : OnDraw tourne sur le fil GPU, OnFrame sur celui de la
|
|
/// presentation. On ne batit aucune conclusion fine dessus -- seulement des ordres de grandeur.
|
|
/// </summary>
|
|
static class MvppScenePass
|
|
{
|
|
public static readonly bool Enabled =
|
|
Environment.GetEnvironmentVariable("RYUJINX_MVPP_SCENEPASS") == "1";
|
|
|
|
private const int MissesBeforeCatchUp = 2;
|
|
private const int CatchUpFrames = 300;
|
|
private const int CatchUpsBeforeDisarm = 5;
|
|
private const int ReportMs = 5000;
|
|
|
|
private static bool _disarmed;
|
|
private static int _missStreak;
|
|
private static int _catchUpLeft;
|
|
private static int _catchUps;
|
|
|
|
private static bool _qualThisFrame;
|
|
|
|
private static int _frames;
|
|
private static int _captured;
|
|
private static int _skips;
|
|
private static int _catchUpFramesSeen;
|
|
private static long _lastReportMs;
|
|
|
|
/// <summary>
|
|
/// Appelee sur un dessin qualifiant tant que l'image n'a pas encore sa camera. Rend false
|
|
/// pour SAUTER la tentative de capture sur ce dessin -- ce qui laisse la capture se faire
|
|
/// plus loin dans la MEME image, sur la passe de la scene.
|
|
/// </summary>
|
|
public static bool Allows(GpuChannel channel)
|
|
{
|
|
_qualThisFrame = true;
|
|
|
|
if (_disarmed || _catchUpLeft > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (IsSceneColorPass(channel))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
_skips++;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reconnait la passe de la scene a la FORME de sa cible couleur : attachee, non carree,
|
|
/// format flottant HDR. Meme critere que <see cref="MvppColorSnapshot"/>. Une cible 8 bits
|
|
/// (interface, composition, passe auxiliaire) et une passe sans couleur du tout echouent
|
|
/// ici -- ce sont exactement les deux contextes qui portaient 96 % des intruses.
|
|
/// </summary>
|
|
internal static bool IsSceneColorPass(GpuChannel channel)
|
|
{
|
|
Image.Texture col0 = channel.TextureManager.RenderTargetColor0;
|
|
|
|
if (col0 == null || col0.Info.Width == col0.Info.Height)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
GAL.Format f = col0.Info.FormatInfo.Format;
|
|
|
|
return f == GAL.Format.R11G11B10Float || f == GAL.Format.R16G16B16A16Float;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Une fois par image presentee, AVANT que le drapeau de capture ne soit re-arme. C'est ici
|
|
/// que vit le filet de securite : une image qui avait des passes qualifiantes et n'a rien
|
|
/// capture est une image potentiellement perdue, et on ne l'accepte pas deux fois de suite.
|
|
/// </summary>
|
|
public static void OnFrame(bool captured)
|
|
{
|
|
if (!_qualThisFrame)
|
|
{
|
|
// Image sans passe 3D (menu, chargement) : elle n'a jamais eu de camera a prendre,
|
|
// la compter comme un manque ferait declencher le rattrapage pour rien.
|
|
return;
|
|
}
|
|
|
|
_qualThisFrame = false;
|
|
_frames++;
|
|
|
|
if (_catchUpLeft > 0)
|
|
{
|
|
_catchUpLeft--;
|
|
_catchUpFramesSeen++;
|
|
}
|
|
|
|
if (captured)
|
|
{
|
|
_captured++;
|
|
_missStreak = 0;
|
|
}
|
|
else if (!_disarmed && _catchUpLeft == 0)
|
|
{
|
|
_missStreak++;
|
|
|
|
if (_missStreak >= MissesBeforeCatchUp)
|
|
{
|
|
_missStreak = 0;
|
|
_catchUps++;
|
|
_catchUpLeft = CatchUpFrames;
|
|
|
|
if (_catchUps >= CatchUpsBeforeDisarm)
|
|
{
|
|
_disarmed = true;
|
|
|
|
Logger.Warning?.Print(LogClass.Gpu,
|
|
$"MVPP SCENEPASS DESARME : {_catchUps} rattrapages, la passe de scene HDR " +
|
|
"n'est pas assez fiable sur ce jeu pour porter la capture. Retour au " +
|
|
"comportement d'avant pour le reste de la session.");
|
|
}
|
|
else
|
|
{
|
|
Logger.Info?.Print(LogClass.Gpu,
|
|
$"MVPP SCENEPASS rattrapage #{_catchUps} : {MissesBeforeCatchUp} images " +
|
|
$"d'affilee sans capture, restriction levee pour {CatchUpFrames} images.");
|
|
}
|
|
}
|
|
}
|
|
|
|
long now = Environment.TickCount64;
|
|
|
|
if (now - _lastReportMs >= ReportMs)
|
|
{
|
|
_lastReportMs = now;
|
|
|
|
Logger.Info?.Print(LogClass.Gpu,
|
|
$"MVPP SCENEPASS : {_frames} images 3D, {_captured} avec camera " +
|
|
$"({(_frames > 0 ? 100f * _captured / _frames : 0f):0.#} %), " +
|
|
$"{_skips} dessins sautes, {_catchUps} rattrapages " +
|
|
$"({_catchUpFramesSeen} images en rattrapage)" +
|
|
$"{(_disarmed ? " · DESARME" : "")}");
|
|
}
|
|
}
|
|
}
|
|
}
|