diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index 518e71ade..8f69eaa74 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -373,7 +373,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _context.Renderer.Pipeline.DrawTexture( texture?.HostTexture, - sampler?.HostSampler, + sampler?.GetHostSampler(texture), new Extents2DF(srcX0, srcY0, srcX1, srcY1), new Extents2DF(dstX0, dstY0, dstX1, dstY1)); } diff --git a/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/Ryujinx.Graphics.Gpu/Image/Sampler.cs index 4c05329ae..52d5ccec1 100644 --- a/Ryujinx.Graphics.Gpu/Image/Sampler.cs +++ b/Ryujinx.Graphics.Gpu/Image/Sampler.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using System; +using System.Numerics; namespace Ryujinx.Graphics.Gpu.Image { @@ -8,10 +9,17 @@ namespace Ryujinx.Graphics.Gpu.Image /// class Sampler : IDisposable { + private const int MinLevelsForAnisotropic = 5; + /// /// Host sampler object. /// - public ISampler HostSampler { get; } + private readonly ISampler _hostSampler; + + /// + /// Host sampler object, with anisotropy forced. + /// + private readonly ISampler _anisoSampler; /// /// Creates a new instance of the cached sampler. @@ -42,13 +50,10 @@ namespace Ryujinx.Graphics.Gpu.Image float maxLod = descriptor.UnpackMaxLod(); float mipLodBias = descriptor.UnpackMipLodBias(); - float maxRequestedAnisotropy = GraphicsConfig.MaxAnisotropy >= 0 && GraphicsConfig.MaxAnisotropy <= 16 ? GraphicsConfig.MaxAnisotropy : descriptor.UnpackMaxAnisotropy(); + float maxRequestedAnisotropy = descriptor.UnpackMaxAnisotropy(); float maxSupportedAnisotropy = context.Capabilities.MaximumSupportedAnisotropy; - if (maxRequestedAnisotropy > maxSupportedAnisotropy) - maxRequestedAnisotropy = maxSupportedAnisotropy; - - HostSampler = context.Renderer.CreateSampler(new SamplerCreateInfo( + _hostSampler = context.Renderer.CreateSampler(new SamplerCreateInfo( minFilter, magFilter, seamlessCubemap, @@ -61,7 +66,56 @@ namespace Ryujinx.Graphics.Gpu.Image minLod, maxLod, mipLodBias, - maxRequestedAnisotropy)); + Math.Min(maxRequestedAnisotropy, maxSupportedAnisotropy))); + + if (GraphicsConfig.MaxAnisotropy >= 0 && GraphicsConfig.MaxAnisotropy <= 16 && (minFilter == MinFilter.LinearMipmapNearest || minFilter == MinFilter.LinearMipmapLinear)) + { + maxRequestedAnisotropy = GraphicsConfig.MaxAnisotropy; + + _anisoSampler = context.Renderer.CreateSampler(new SamplerCreateInfo( + minFilter, + magFilter, + seamlessCubemap, + addressU, + addressV, + addressP, + compareMode, + compareOp, + color, + minLod, + maxLod, + mipLodBias, + Math.Min(maxRequestedAnisotropy, maxSupportedAnisotropy))); + } + } + + /// + /// Gets a host sampler for the given texture. + /// + /// Texture to be sampled + /// A host sampler + public ISampler GetHostSampler(Texture texture) + { + return _anisoSampler != null && AllowForceAnisotropy(texture) ? _anisoSampler : _hostSampler; + } + + /// + /// Determine if the given texture can have anisotropic filtering forced. + /// Filtered textures that we might want to force anisotropy on should have a lot of mip levels. + /// + /// The texture + /// True if anisotropic filtering can be forced, false otherwise + private static bool AllowForceAnisotropy(Texture texture) + { + if (texture == null || !(texture.Target == Target.Texture2D || texture.Target == Target.Texture2DArray)) + { + return false; + } + + int maxSize = Math.Max(texture.Info.Width, texture.Info.Height); + int maxLevels = BitOperations.Log2((uint)maxSize) + 1; + + return texture.Info.Levels >= Math.Min(MinLevelsForAnisotropic, maxLevels); } /// @@ -69,7 +123,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// public void Dispose() { - HostSampler.Dispose(); + _hostSampler.Dispose(); + _anisoSampler?.Dispose(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs index 5a84bd847..e205ec487 100644 --- a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs +++ b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// class SamplerPool : Pool { + private float _forcedAnisotropy; + /// /// Constructs a new instance of the sampler pool. /// @@ -14,7 +16,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// Physical memory where the sampler descriptors are mapped /// Address of the sampler pool in guest memory /// Maximum sampler ID of the sampler pool (equal to maximum samplers minus one) - public SamplerPool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId) { } + public SamplerPool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId) + { + _forcedAnisotropy = GraphicsConfig.MaxAnisotropy; + } /// /// Gets the sampler with the given ID. @@ -30,6 +35,21 @@ namespace Ryujinx.Graphics.Gpu.Image if (SequenceNumber != Context.SequenceNumber) { + if (_forcedAnisotropy != GraphicsConfig.MaxAnisotropy) + { + _forcedAnisotropy = GraphicsConfig.MaxAnisotropy; + + for (int i = 0; i < Items.Length; i++) + { + if (Items[i] != null) + { + Items[i].Dispose(); + + Items[i] = null; + } + } + } + SequenceNumber = Context.SequenceNumber; SynchronizeMemory(); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 621dc2e74..e7561f7df 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -399,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Image Sampler sampler = samplerPool?.Get(samplerId); - ISampler hostSampler = sampler?.HostSampler; + ISampler hostSampler = sampler?.GetHostSampler(texture); if (_textureState[stageIndex][index].Sampler != hostSampler || _rebind) {