From 0a80a837cb30402cad1f41293134edbaeeec6451 Mon Sep 17 00:00:00 2001
From: riperiperi <rhy3756547@hotmail.com>
Date: Wed, 11 Aug 2021 21:44:51 +0100
Subject: [PATCH] Use "Undesired" scale mode for certain textures rather than
 blacklisting (#2537)

* Use "Undesired" scale mode for certain textures rather than blacklisting

* Nit

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
---
 .../Engine/Threed/StateUpdater.cs             | 11 ++++--
 Ryujinx.Graphics.Gpu/Image/Texture.cs         |  3 +-
 Ryujinx.Graphics.Gpu/Image/TextureCache.cs    | 36 +++++++++++--------
 Ryujinx.Graphics.Gpu/Image/TextureManager.cs  | 23 +++++++++---
 .../Image/TextureScaleMode.cs                 |  4 ++-
 5 files changed, 53 insertions(+), 24 deletions(-)

diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index fb4ab007c2..da412bdf2c 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -380,11 +380,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
 
             if (changedScale)
             {
+                float oldScale = _channel.TextureManager.RenderTargetScale;
                 _channel.TextureManager.UpdateRenderTargetScale(singleUse);
-                _context.Renderer.Pipeline.SetRenderTargetScale(_channel.TextureManager.RenderTargetScale);
 
-                UpdateViewportTransform();
-                UpdateScissorState();
+                if (oldScale != _channel.TextureManager.RenderTargetScale)
+                {
+                    _context.Renderer.Pipeline.SetRenderTargetScale(_channel.TextureManager.RenderTargetScale);
+
+                    UpdateViewportTransform();
+                    UpdateScissorState();
+                }
             }
         }
 
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index a3105cf285..e156ff5eda 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -549,7 +549,8 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <param name="scale">The new scale factor for this texture</param>
         public void SetScale(float scale)
         {
-            TextureScaleMode newScaleMode = ScaleMode == TextureScaleMode.Blacklisted ? ScaleMode : TextureScaleMode.Scaled;
+            bool unscaled = ScaleMode == TextureScaleMode.Blacklisted || (ScaleMode == TextureScaleMode.Undesired && scale == 1);
+            TextureScaleMode newScaleMode = unscaled ? ScaleMode : TextureScaleMode.Scaled;
 
             if (_viewStorage != this)
             {
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index d9920f9732..58cd3a2f7c 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -87,10 +87,16 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// Determines if a given texture is eligible for upscaling from its info.
         /// </summary>
         /// <param name="info">The texture info to check</param>
+        /// <param name="withUpscale">True if the user of the texture would prefer it to be upscaled immediately</param>
         /// <returns>True if eligible</returns>
-        private static bool IsUpscaleCompatible(TextureInfo info)
+        private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale)
         {
-            return (info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && !info.FormatInfo.IsCompressed && UpscaleSafeMode(info);
+            if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && !info.FormatInfo.IsCompressed)
+            {
+                return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired;
+            }
+
+            return TextureScaleMode.Blacklisted;
         }
 
         /// <summary>
@@ -117,13 +123,13 @@ namespace Ryujinx.Graphics.Gpu.Image
                 return false;
             }
 
+            int widthAlignment = (info.IsLinear ? Constants.StrideAlignment : Constants.GobAlignment) / info.FormatInfo.BytesPerPixel;
+
             if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Components == 1))
             {
                 // Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas)
                 // Detect if the texture is possibly square. Widths may be aligned, so to remove the uncertainty we align both the width and height.
 
-                int widthAlignment = (info.IsLinear ? Constants.StrideAlignment : Constants.GobAlignment) / info.FormatInfo.BytesPerPixel;
-
                 bool possiblySquare = BitUtils.AlignUp(info.Width, widthAlignment) == BitUtils.AlignUp(info.Height, widthAlignment);
 
                 if (possiblySquare)
@@ -132,11 +138,17 @@ namespace Ryujinx.Graphics.Gpu.Image
                 }
             }
 
-            int aspect = (int)Math.Round((info.Width / (float)info.Height) * 9);
-            if (aspect == 16 && info.Height < 360)
+            if (info.Height < 360)
             {
-                // Targets that are roughly 16:9 can only be rescaled if they're equal to or above 360p. (excludes blur and bloom textures)
-                return false;
+                int aspectWidth = (int)MathF.Ceiling((info.Height / 9f) * 16f);
+                int aspectMaxWidth = BitUtils.AlignUp(aspectWidth, widthAlignment);
+                int aspectMinWidth = BitUtils.AlignDown(aspectWidth, widthAlignment);
+
+                if (info.Width >= aspectMinWidth && info.Width <= aspectMaxWidth && info.Height < 360)
+                {
+                    // Targets that are roughly 16:9 can only be rescaled if they're equal to or above 360p. (excludes blur and bloom textures)
+                    return false;
+                }
             }
 
             return true;
@@ -354,13 +366,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         {
             bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
 
-            bool isScalable = IsUpscaleCompatible(info);
-
-            TextureScaleMode scaleMode = TextureScaleMode.Blacklisted;
-            if (isScalable)
-            {
-                scaleMode = (flags & TextureSearchFlags.WithUpscale) != 0 ? TextureScaleMode.Scaled : TextureScaleMode.Eligible;
-            }
+            TextureScaleMode scaleMode = IsUpscaleCompatible(info, (flags & TextureSearchFlags.WithUpscale) != 0);
 
             ulong address;
 
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 157b7c17ef..4db6532b81 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -140,6 +140,16 @@ namespace Ryujinx.Graphics.Gpu.Image
             _gpBindingsManager.SetTexturePool(gpuVa, maximumId);
         }
 
+        /// <summary>
+        /// Check if a texture's scale must be updated to match the configured resolution scale.
+        /// </summary>
+        /// <param name="texture">The texture to check</param>
+        /// <returns>True if the scale needs updating, false if the scale is up to date</returns>
+        private bool ScaleNeedsUpdated(Texture texture)
+        {
+            return texture != null && !(texture.ScaleMode == TextureScaleMode.Blacklisted || texture.ScaleMode == TextureScaleMode.Undesired) && texture.ScaleFactor != GraphicsConfig.ResScale;
+        }
+
         /// <summary>
         /// Sets the render target color buffer.
         /// </summary>
@@ -164,7 +174,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                 _rtColors[index] = color;
             }
 
-            return changesScale || (hasValue && color.ScaleMode != TextureScaleMode.Blacklisted && color.ScaleFactor != GraphicsConfig.ResScale);
+            return changesScale || ScaleNeedsUpdated(color);
         }
 
         /// <summary>
@@ -190,7 +200,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                 _rtDepthStencil = depthStencil;
             }
 
-            return changesScale || (hasValue && depthStencil.ScaleMode != TextureScaleMode.Blacklisted && depthStencil.ScaleFactor != GraphicsConfig.ResScale);
+            return changesScale || ScaleNeedsUpdated(depthStencil);
         }
 
         /// <summary>
@@ -214,6 +224,7 @@ namespace Ryujinx.Graphics.Gpu.Image
             bool mismatch = false;
             bool blacklisted = false;
             bool hasUpscaled = false;
+            bool hasUndesired = false;
             float targetScale = GraphicsConfig.ResScale;
 
             void ConsiderTarget(Texture target)
@@ -230,9 +241,13 @@ namespace Ryujinx.Graphics.Gpu.Image
                     case TextureScaleMode.Eligible:
                         mismatch = true; // We must make a decision.
                         break;
+                    case TextureScaleMode.Undesired:
+                        hasUndesired = true;
+                        mismatch |= scale != 1f || hasUpscaled; // If another target is upscaled, scale this one up too.
+                        break;
                     case TextureScaleMode.Scaled:
                         hasUpscaled = true;
-                        mismatch |= scale != targetScale; // If the target scale has changed, reset the scale for all targets.
+                        mismatch |= hasUndesired || scale != targetScale; // If the target scale has changed, reset the scale for all targets.
                         break;
                 }
             }
@@ -254,7 +269,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             mismatch |= blacklisted && hasUpscaled;
 
-            if (blacklisted)
+            if (blacklisted || (hasUndesired && !hasUpscaled))
             {
                 targetScale = 1f;
             }
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs b/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs
index 2c9e431dc8..b937f57781 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs
@@ -4,11 +4,13 @@
     /// The scale mode for a given texture.
     /// Blacklisted textures cannot be scaled, Eligible textures have not been scaled yet,
     /// and Scaled textures have been scaled already.
+    /// Undesired textures will stay at 1x until a situation where they must match a scaled texture.
     /// </summary>
     enum TextureScaleMode
     {
         Eligible = 0,
         Scaled = 1,
-        Blacklisted = 2
+        Blacklisted = 2,
+        Undesired = 3
     }
 }