diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs index d2b6bec3..4ce53e78 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs @@ -349,6 +349,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod return; } + if (srcTexture.Info.Samples > 1 || dstTexture.Info.Samples > 1) + { + srcTexture.PropagateScale(dstTexture); + } + float scale = srcTexture.ScaleFactor; float dstScale = dstTexture.ScaleFactor; diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index cfe57756..5ed9b2a0 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -25,6 +25,12 @@ namespace Ryujinx.Graphics.Gpu.Image // This method uses much more memory so we want to avoid it if possible. private const int ByteComparisonSwitchThreshold = 4; + // Tuning for blacklisting textures from scaling when their data is updated from CPU. + // Each write adds the weight, each GPU modification subtracts 1. + // Exceeding the threshold blacklists the texture. + private const int ScaledSetWeight = 10; + private const int ScaledSetThreshold = 30; + private const int MinLevelsForForceAnisotropy = 5; private struct TexturePoolOwner @@ -122,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Image private Target _arrayViewTarget; private ITexture _flushHostTexture; + private ITexture _setHostTexture; + private int _scaledSetScore; private Texture _viewStorage; @@ -518,6 +526,25 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Registers when a texture has had its data set after being scaled, and + /// determines if it should be blacklisted from scaling to improve performance. + /// + /// True if setting data for a scaled texture is allowed, false if the texture has been blacklisted + private bool AllowScaledSetData() + { + _scaledSetScore += ScaledSetWeight; + + if (_scaledSetScore >= ScaledSetThreshold) + { + BlacklistScale(); + + return false; + } + + return true; + } + /// /// Blacklists this texture from being scaled. Resets its scale to 1 if needed. /// @@ -554,9 +581,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale. /// /// Scale factor + /// True if the data should be copied to the texture, false otherwise /// Texture to use instead of creating one /// A host texture containing a scaled version of this texture - private ITexture GetScaledHostTexture(float scale, ITexture storage = null) + private ITexture GetScaledHostTexture(float scale, bool copy, ITexture storage = null) { if (storage == null) { @@ -564,7 +592,10 @@ namespace Ryujinx.Graphics.Gpu.Image storage = _context.Renderer.CreateTexture(createInfo, scale); } - HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true); + if (copy) + { + HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true); + } return storage; } @@ -595,7 +626,7 @@ namespace Ryujinx.Graphics.Gpu.Image ScaleFactor = scale; - ITexture newStorage = GetScaledHostTexture(ScaleFactor); + ITexture newStorage = GetScaledHostTexture(ScaleFactor, true); Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}"); @@ -692,11 +723,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// public void SynchronizeFull() { - if (_hasData) - { - BlacklistScale(); - } - ReadOnlySpan data = _physicalMemory.GetSpan(Range); // If the host does not support ASTC compression, we need to do the decompression. @@ -723,7 +749,19 @@ namespace Ryujinx.Graphics.Gpu.Image SpanOrArray result = ConvertToHostCompatibleFormat(data); - HostTexture.SetData(result); + if (ScaleFactor != 1f && AllowScaledSetData()) + { + // If needed, create a texture to load from 1x scale. + ITexture texture = _setHostTexture = GetScaledHostTexture(1f, false, _setHostTexture); + + texture.SetData(result); + + texture.CopyTo(HostTexture, new Extents2D(0, 0, texture.Width, texture.Height), new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), true); + } + else + { + HostTexture.SetData(result); + } _hasData = true; } @@ -1056,7 +1094,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (ScaleFactor != 1f) { // If needed, create a texture to flush back to host at 1x scale. - texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture); + texture = _flushHostTexture = GetScaledHostTexture(1f, true, _flushHostTexture); } return texture; @@ -1456,6 +1494,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// public void SignalModified() { + _scaledSetScore = Math.Max(0, _scaledSetScore - 1); + if (_modifiedStale || Group.HasCopyDependencies) { _modifiedStale = false; @@ -1472,6 +1512,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if the texture has been bound, false if it has been unbound public void SignalModifying(bool bound) { + if (bound) + { + _scaledSetScore = Math.Max(0, _scaledSetScore - 1); + } + if (_modifiedStale || Group.HasCopyDependencies) { _modifiedStale = false; @@ -1685,6 +1730,9 @@ namespace Ryujinx.Graphics.Gpu.Image _flushHostTexture?.Release(); _flushHostTexture = null; + + _setHostTexture?.Release(); + _setHostTexture = null; } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 49adecdc..9802a3dc 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if eligible private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale) { - if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && !info.FormatInfo.IsCompressed) + if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray || info.Target == Target.Texture2DMultisample) && !info.FormatInfo.IsCompressed) { return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired; }