Ryujinx/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
riperiperi 788aec511f
Limit Custom Anisotropic Filtering to mipmapped textures with many levels (#2832)
* Limit Custom Anisotropic Filtering to only fully mipmapped textures

There's a major flaw with the anisotropic filtering setting that causes @GamerzHell9137 to report graphical bugs that otherwise wouldn't be there, because he just won't set it to Auto. This should fix those issues, hopefully.

These bugs are generally because anisotropic filtering is enabled on something that it shouldn't be, such as a post process filter or some data texture. This PR maintains two host samplers when custom AF is enabled, and only uses the forced AF one when the texture is 2d and fully mipmapped (goes down to 1x1). This is because game textures are the ideal target for this filtering, and they are typically fully mipmapped, unlike things like screen render targets which usually have 1 or just a few levels.

This also only enables AF on mipmapped samplers where the filtering is bilinear or trilinear. This should be self explanatory.

This PR also allows the changing of Anisotropic Filtering at runtime, and you can immediately see the changes. All samplers are flushed from the cache if the setting changes, causing them to be recreated with the new custom AF value. This brings it in line with our resolution scale. 😌

* Expected minimum mip count for large textures rather than all, address feedback

* Use Target rather than Info.Target

* Retrigger build?

* Fix rebase
2021-11-13 16:04:21 -03:00

116 lines
3.7 KiB
C#

using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image
{
/// <summary>
/// Sampler pool.
/// </summary>
class SamplerPool : Pool<Sampler, SamplerDescriptor>
{
private float _forcedAnisotropy;
/// <summary>
/// Constructs a new instance of the sampler pool.
/// </summary>
/// <param name="context">GPU context that the sampler pool belongs to</param>
/// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param>
/// <param name="address">Address of the sampler pool in guest memory</param>
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
public SamplerPool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId)
{
_forcedAnisotropy = GraphicsConfig.MaxAnisotropy;
}
/// <summary>
/// Gets the sampler with the given ID.
/// </summary>
/// <param name="id">ID of the sampler. This is effectively a zero-based index</param>
/// <returns>The sampler with the given ID</returns>
public override Sampler Get(int id)
{
if ((uint)id >= Items.Length)
{
return null;
}
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();
}
Sampler sampler = Items[id];
if (sampler == null)
{
SamplerDescriptor descriptor = GetDescriptor(id);
sampler = new Sampler(Context, descriptor);
Items[id] = sampler;
DescriptorCache[id] = descriptor;
}
return sampler;
}
/// <summary>
/// Implementation of the sampler pool range invalidation.
/// </summary>
/// <param name="address">Start address of the range of the sampler pool</param>
/// <param name="size">Size of the range being invalidated</param>
protected override void InvalidateRangeImpl(ulong address, ulong size)
{
ulong endAddress = address + size;
for (; address < endAddress; address += DescriptorSize)
{
int id = (int)((address - Address) / DescriptorSize);
Sampler sampler = Items[id];
if (sampler != null)
{
SamplerDescriptor descriptor = GetDescriptor(id);
// If the descriptors are the same, the sampler is still valid.
if (descriptor.Equals(ref DescriptorCache[id]))
{
continue;
}
sampler.Dispose();
Items[id] = null;
}
}
}
/// <summary>
/// Deletes a given sampler pool entry.
/// The host memory used by the sampler is released by the driver.
/// </summary>
/// <param name="item">The entry to be deleted</param>
protected override void Delete(Sampler item)
{
item?.Dispose();
}
}
}