Initial work

This commit is contained in:
gdk 2019-10-13 03:02:07 -03:00 committed by Thog
parent f617fb542a
commit 1876b346fe
518 changed files with 15170 additions and 12486 deletions

View file

@ -0,0 +1,62 @@
using System.Collections;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image
{
class AutoDeleteCache : IEnumerable<Texture>
{
private const int MaxCapacity = 2048;
private LinkedList<Texture> _textures;
public AutoDeleteCache()
{
_textures = new LinkedList<Texture>();
}
public void Add(Texture texture)
{
texture.IncrementReferenceCount();
texture.CacheNode = _textures.AddLast(texture);
if (_textures.Count > MaxCapacity)
{
Texture oldestTexture = _textures.First.Value;
_textures.RemoveFirst();
oldestTexture.DecrementReferenceCount();
oldestTexture.CacheNode = null;
}
}
public void Lift(Texture texture)
{
if (texture.CacheNode != null)
{
if (texture.CacheNode != _textures.Last)
{
_textures.Remove(texture.CacheNode);
texture.CacheNode = _textures.AddLast(texture);
}
}
else
{
Add(texture);
}
}
public IEnumerator<Texture> GetEnumerator()
{
return _textures.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _textures.GetEnumerator();
}
}
}

View file

@ -0,0 +1,31 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.Image
{
struct FormatInfo
{
private static FormatInfo _rgba8 = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
public static FormatInfo Default => _rgba8;
public Format Format { get; }
public int BlockWidth { get; }
public int BlockHeight { get; }
public int BytesPerPixel { get; }
public bool IsCompressed => (BlockWidth | BlockHeight) != 1;
public FormatInfo(
Format format,
int blockWidth,
int blockHeight,
int bytesPerPixel)
{
Format = format;
BlockWidth = blockWidth;
BlockHeight = blockHeight;
BytesPerPixel = bytesPerPixel;
}
}
}

View file

@ -0,0 +1,201 @@
using Ryujinx.Graphics.GAL;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image
{
static class FormatTable
{
private static Dictionary<uint, FormatInfo> _textureFormats = new Dictionary<uint, FormatInfo>()
{
{ 0x2491d, new FormatInfo(Format.R8Unorm, 1, 1, 1) },
{ 0x1249d, new FormatInfo(Format.R8Snorm, 1, 1, 1) },
{ 0x4921d, new FormatInfo(Format.R8Uint, 1, 1, 1) },
{ 0x36d9d, new FormatInfo(Format.R8Sint, 1, 1, 1) },
{ 0x7ff9b, new FormatInfo(Format.R16Float, 1, 1, 2) },
{ 0x2491b, new FormatInfo(Format.R16Unorm, 1, 1, 2) },
{ 0x1249b, new FormatInfo(Format.R16Snorm, 1, 1, 2) },
{ 0x4921b, new FormatInfo(Format.R16Uint, 1, 1, 2) },
{ 0x36d9b, new FormatInfo(Format.R16Sint, 1, 1, 2) },
{ 0x7ff8f, new FormatInfo(Format.R32Float, 1, 1, 4) },
{ 0x4920f, new FormatInfo(Format.R32Uint, 1, 1, 4) },
{ 0x36d8f, new FormatInfo(Format.R32Sint, 1, 1, 4) },
{ 0x24918, new FormatInfo(Format.R8G8Unorm, 1, 1, 2) },
{ 0x12498, new FormatInfo(Format.R8G8Snorm, 1, 1, 2) },
{ 0x49218, new FormatInfo(Format.R8G8Uint, 1, 1, 2) },
{ 0x36d98, new FormatInfo(Format.R8G8Sint, 1, 1, 2) },
{ 0x7ff8c, new FormatInfo(Format.R16G16Float, 1, 1, 4) },
{ 0x2490c, new FormatInfo(Format.R16G16Unorm, 1, 1, 4) },
{ 0x1248c, new FormatInfo(Format.R16G16Snorm, 1, 1, 4) },
{ 0x4920c, new FormatInfo(Format.R16G16Uint, 1, 1, 4) },
{ 0x36d8c, new FormatInfo(Format.R16G16Sint, 1, 1, 4) },
{ 0x7ff84, new FormatInfo(Format.R32G32Float, 1, 1, 8) },
{ 0x49204, new FormatInfo(Format.R32G32Uint, 1, 1, 8) },
{ 0x36d84, new FormatInfo(Format.R32G32Sint, 1, 1, 8) },
{ 0x7ff82, new FormatInfo(Format.R32G32B32Float, 1, 1, 12) },
{ 0x49202, new FormatInfo(Format.R32G32B32Uint, 1, 1, 12) },
{ 0x36d82, new FormatInfo(Format.R32G32B32Sint, 1, 1, 12) },
{ 0x24908, new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4) },
{ 0x12488, new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4) },
{ 0x49208, new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4) },
{ 0x36d88, new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4) },
{ 0x7ff83, new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8) },
{ 0x24903, new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8) },
{ 0x12483, new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8) },
{ 0x49203, new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8) },
{ 0x36d83, new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8) },
{ 0x7ff81, new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16) },
{ 0x49201, new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16) },
{ 0x36d81, new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16) },
{ 0x2493a, new FormatInfo(Format.D16Unorm, 1, 1, 2) },
{ 0x7ffaf, new FormatInfo(Format.D32Float, 1, 1, 4) },
{ 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4) },
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8) },
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4) },
{ 0x24912, new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2) },
{ 0x24914, new FormatInfo(Format.R5G5B5A1Unorm, 1, 1, 2) },
{ 0x24915, new FormatInfo(Format.R5G6B5Unorm, 1, 1, 2) },
{ 0x24909, new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4) },
{ 0x49209, new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4) },
{ 0x7ffa1, new FormatInfo(Format.R11G11B10Float, 1, 1, 4) },
{ 0x7ffa0, new FormatInfo(Format.R9G9B9E5Float, 1, 1, 4) },
{ 0x24924, new FormatInfo(Format.Bc1RgbaUnorm, 4, 4, 8) },
{ 0x24925, new FormatInfo(Format.Bc2Unorm, 4, 4, 16) },
{ 0x24926, new FormatInfo(Format.Bc3Unorm, 4, 4, 16) },
{ 0xa4924, new FormatInfo(Format.Bc1RgbaSrgb, 4, 4, 8) },
{ 0xa4925, new FormatInfo(Format.Bc2Srgb, 4, 4, 16) },
{ 0xa4926, new FormatInfo(Format.Bc3Srgb, 4, 4, 16) },
{ 0x24927, new FormatInfo(Format.Bc4Unorm, 4, 4, 8) },
{ 0x124a7, new FormatInfo(Format.Bc4Snorm, 4, 4, 8) },
{ 0x24928, new FormatInfo(Format.Bc5Unorm, 4, 4, 16) },
{ 0x124a8, new FormatInfo(Format.Bc5Snorm, 4, 4, 16) },
{ 0x24917, new FormatInfo(Format.Bc7Unorm, 4, 4, 16) },
{ 0xa4917, new FormatInfo(Format.Bc7Srgb, 4, 4, 16) },
{ 0x7ff90, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16) },
{ 0x7ff91, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16) },
{ 0x24940, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16) },
{ 0x24950, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16) },
{ 0x24941, new FormatInfo(Format.Astc5x5Unorm, 5, 5, 16) },
{ 0x24951, new FormatInfo(Format.Astc6x5Unorm, 6, 5, 16) },
{ 0x24942, new FormatInfo(Format.Astc6x6Unorm, 6, 6, 16) },
{ 0x24955, new FormatInfo(Format.Astc8x5Unorm, 8, 5, 16) },
{ 0x24952, new FormatInfo(Format.Astc8x6Unorm, 8, 6, 16) },
{ 0x24944, new FormatInfo(Format.Astc8x8Unorm, 8, 8, 16) },
{ 0x24956, new FormatInfo(Format.Astc10x5Unorm, 10, 5, 16) },
{ 0x24957, new FormatInfo(Format.Astc10x6Unorm, 10, 6, 16) },
{ 0x24953, new FormatInfo(Format.Astc10x8Unorm, 10, 8, 16) },
{ 0x24945, new FormatInfo(Format.Astc10x10Unorm, 10, 10, 16) },
{ 0x24954, new FormatInfo(Format.Astc12x10Unorm, 12, 10, 16) },
{ 0x24946, new FormatInfo(Format.Astc12x12Unorm, 12, 12, 16) },
{ 0xa4940, new FormatInfo(Format.Astc4x4Srgb, 4, 4, 16) },
{ 0xa4950, new FormatInfo(Format.Astc5x4Srgb, 5, 4, 16) },
{ 0xa4941, new FormatInfo(Format.Astc5x5Srgb, 5, 5, 16) },
{ 0xa4951, new FormatInfo(Format.Astc6x5Srgb, 6, 5, 16) },
{ 0xa4942, new FormatInfo(Format.Astc6x6Srgb, 6, 6, 16) },
{ 0xa4955, new FormatInfo(Format.Astc8x5Srgb, 8, 5, 16) },
{ 0xa4952, new FormatInfo(Format.Astc8x6Srgb, 8, 6, 16) },
{ 0xa4944, new FormatInfo(Format.Astc8x8Srgb, 8, 8, 16) },
{ 0xa4956, new FormatInfo(Format.Astc10x5Srgb, 10, 5, 16) },
{ 0xa4957, new FormatInfo(Format.Astc10x6Srgb, 10, 6, 16) },
{ 0xa4953, new FormatInfo(Format.Astc10x8Srgb, 10, 8, 16) },
{ 0xa4945, new FormatInfo(Format.Astc10x10Srgb, 10, 10, 16) },
{ 0xa4954, new FormatInfo(Format.Astc12x10Srgb, 12, 10, 16) },
{ 0xa4946, new FormatInfo(Format.Astc12x12Srgb, 12, 12, 16) },
{ 0x24913, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2) }
};
private static Dictionary<ulong, Format> _attribFormats = new Dictionary<ulong, Format>()
{
{ 0x13a00000, Format.R8Unorm },
{ 0x0ba00000, Format.R8Snorm },
{ 0x23a00000, Format.R8Uint },
{ 0x1ba00000, Format.R8Sint },
{ 0x3b600000, Format.R16Float },
{ 0x13600000, Format.R16Unorm },
{ 0x0b600000, Format.R16Snorm },
{ 0x23600000, Format.R16Uint },
{ 0x1b600000, Format.R16Sint },
{ 0x3a400000, Format.R32Float },
{ 0x22400000, Format.R32Uint },
{ 0x1a400000, Format.R32Sint },
{ 0x13000000, Format.R8G8Unorm },
{ 0x0b000000, Format.R8G8Snorm },
{ 0x23000000, Format.R8G8Uint },
{ 0x1b000000, Format.R8G8Sint },
{ 0x39e00000, Format.R16G16Float },
{ 0x11e00000, Format.R16G16Unorm },
{ 0x09e00000, Format.R16G16Snorm },
{ 0x21e00000, Format.R16G16Uint },
{ 0x19e00000, Format.R16G16Sint },
{ 0x38800000, Format.R32G32Float },
{ 0x20800000, Format.R32G32Uint },
{ 0x18800000, Format.R32G32Sint },
{ 0x12600000, Format.R8G8B8Unorm },
{ 0x0a600000, Format.R8G8B8Snorm },
{ 0x22600000, Format.R8G8B8Uint },
{ 0x1a600000, Format.R8G8B8Sint },
{ 0x38a00000, Format.R16G16B16Float },
{ 0x10a00000, Format.R16G16B16Unorm },
{ 0x08a00000, Format.R16G16B16Snorm },
{ 0x20a00000, Format.R16G16B16Uint },
{ 0x18a00000, Format.R16G16B16Sint },
{ 0x38400000, Format.R32G32B32Float },
{ 0x20400000, Format.R32G32B32Uint },
{ 0x18400000, Format.R32G32B32Sint },
{ 0x11400000, Format.R8G8B8A8Unorm },
{ 0x09400000, Format.R8G8B8A8Snorm },
{ 0x21400000, Format.R8G8B8A8Uint },
{ 0x19400000, Format.R8G8B8A8Sint },
{ 0x38600000, Format.R16G16B16A16Float },
{ 0x10600000, Format.R16G16B16A16Unorm },
{ 0x08600000, Format.R16G16B16A16Snorm },
{ 0x20600000, Format.R16G16B16A16Uint },
{ 0x18600000, Format.R16G16B16A16Sint },
{ 0x38200000, Format.R32G32B32A32Float },
{ 0x20200000, Format.R32G32B32A32Uint },
{ 0x18200000, Format.R32G32B32A32Sint },
{ 0x16000000, Format.R10G10B10A2Unorm },
{ 0x26000000, Format.R10G10B10A2Uint },
{ 0x3e200000, Format.R11G11B10Float },
{ 0x2ba00000, Format.R8Uscaled },
{ 0x33a00000, Format.R8Sscaled },
{ 0x2b600000, Format.R16Uscaled },
{ 0x33600000, Format.R16Sscaled },
{ 0x2a400000, Format.R32Uscaled },
{ 0x32400000, Format.R32Sscaled },
{ 0x2b000000, Format.R8G8Uscaled },
{ 0x33000000, Format.R8G8Sscaled },
{ 0x29e00000, Format.R16G16Uscaled },
{ 0x31e00000, Format.R16G16Sscaled },
{ 0x28800000, Format.R32G32Uscaled },
{ 0x30800000, Format.R32G32Sscaled },
{ 0x2a600000, Format.R8G8B8Uscaled },
{ 0x32600000, Format.R8G8B8Sscaled },
{ 0x28a00000, Format.R16G16B16Uscaled },
{ 0x30a00000, Format.R16G16B16Sscaled },
{ 0x28400000, Format.R32G32B32Uscaled },
{ 0x30400000, Format.R32G32B32Sscaled },
{ 0x29400000, Format.R8G8B8A8Uscaled },
{ 0x31400000, Format.R8G8B8A8Sscaled },
{ 0x28600000, Format.R16G16B16A16Uscaled },
{ 0x30600000, Format.R16G16B16A16Sscaled },
{ 0x28200000, Format.R32G32B32A32Uscaled },
{ 0x30200000, Format.R32G32B32A32Sscaled },
{ 0x0e000000, Format.R10G10B10A2Snorm },
{ 0x1e000000, Format.R10G10B10A2Sint },
{ 0x2e000000, Format.R10G10B10A2Uscaled },
{ 0x36000000, Format.R10G10B10A2Sscaled }
};
public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
{
encoded |= (isSrgb ? 1u << 19 : 0u);
return _textureFormats.TryGetValue(encoded, out format);
}
public static bool TryGetAttribFormat(uint encoded, out Format format)
{
return _attribFormats.TryGetValue(encoded, out format);
}
}
}

View file

@ -0,0 +1,99 @@
using System;
namespace Ryujinx.Graphics.Gpu.Image
{
abstract class Pool<T> : IDisposable
{
protected const int DescriptorSize = 0x20;
protected GpuContext Context;
protected T[] Items;
public ulong Address { get; }
public ulong Size { get; }
public Pool(GpuContext context, ulong address, int maximumId)
{
Context = context;
int count = maximumId + 1;
ulong size = (ulong)(uint)count * DescriptorSize;;
Items = new T[count];
Address = address;
Size = size;
}
public abstract T Get(int id);
public void SynchronizeMemory()
{
(ulong, ulong)[] modifiedRanges = Context.PhysicalMemory.GetModifiedRanges(Address, Size);
for (int index = 0; index < modifiedRanges.Length; index++)
{
(ulong mAddress, ulong mSize) = modifiedRanges[index];
if (mAddress < Address)
{
mAddress = Address;
}
ulong maxSize = Address + Size - mAddress;
if (mSize > maxSize)
{
mSize = maxSize;
}
InvalidateRangeImpl(mAddress, mSize);
}
}
public void InvalidateRange(ulong address, ulong size)
{
ulong endAddress = address + size;
ulong texturePoolEndAddress = Address + Size;
// If the range being invalidated is not overlapping the texture pool range,
// then we don't have anything to do, exit early.
if (address >= texturePoolEndAddress || endAddress <= Address)
{
return;
}
if (address < Address)
{
address = Address;
}
if (endAddress > texturePoolEndAddress)
{
endAddress = texturePoolEndAddress;
}
InvalidateRangeImpl(address, size);
}
protected abstract void InvalidateRangeImpl(ulong address, ulong size);
protected abstract void Delete(T item);
public void Dispose()
{
if (Items != null)
{
for (int index = 0; index < Items.Length; index++)
{
Delete(Items[index]);
}
Items = null;
}
}
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gpu.Image
{
enum ReductionFilter
{
Average,
Minimum,
Maximum
}
}

View file

@ -0,0 +1,52 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Color;
using Ryujinx.Graphics.GAL.Sampler;
using System;
namespace Ryujinx.Graphics.Gpu.Image
{
class Sampler : IDisposable
{
public ISampler HostSampler { get; }
public Sampler(GpuContext context, SamplerDescriptor descriptor)
{
MinFilter minFilter = descriptor.UnpackMinFilter();
MagFilter magFilter = descriptor.UnpackMagFilter();
AddressMode addressU = descriptor.UnpackAddressU();
AddressMode addressV = descriptor.UnpackAddressV();
AddressMode addressP = descriptor.UnpackAddressP();
CompareMode compareMode = descriptor.UnpackCompareMode();
CompareOp compareOp = descriptor.UnpackCompareOp();
ColorF color = new ColorF(0, 0, 0, 0);
float minLod = descriptor.UnpackMinLod();
float maxLod = descriptor.UnpackMaxLod();
float mipLodBias = descriptor.UnpackMipLodBias();
float maxAnisotropy = descriptor.UnpackMaxAnisotropy();
HostSampler = context.Renderer.CreateSampler(new SamplerCreateInfo(
minFilter,
magFilter,
addressU,
addressV,
addressP,
compareMode,
compareOp,
color,
minLod,
maxLod,
mipLodBias,
maxAnisotropy));
}
public void Dispose()
{
HostSampler.Dispose();
}
}
}

View file

@ -0,0 +1,132 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Sampler;
namespace Ryujinx.Graphics.Gpu.Image
{
struct SamplerDescriptor
{
private static readonly float[] _f5ToF32ConversionLut = new float[]
{
0.0f,
0.055555556f,
0.1f,
0.13636364f,
0.16666667f,
0.1923077f,
0.21428572f,
0.23333333f,
0.25f,
0.2777778f,
0.3f,
0.3181818f,
0.33333334f,
0.34615386f,
0.35714287f,
0.36666667f,
0.375f,
0.3888889f,
0.4f,
0.4090909f,
0.41666666f,
0.42307693f,
0.42857143f,
0.43333334f,
0.4375f,
0.44444445f,
0.45f,
0.45454547f,
0.45833334f,
0.46153846f,
0.4642857f,
0.46666667f
};
private static readonly float[] _maxAnisotropyLut = new float[]
{
1, 2, 4, 6, 8, 10, 12, 16
};
private const float Frac8ToF32 = 1.0f / 256.0f;
public uint Word0;
public uint Word1;
public uint Word2;
public uint Word3;
public uint BorderColorR;
public uint BorderColorG;
public uint BorderColorB;
public uint BorderColorA;
public AddressMode UnpackAddressU()
{
return (AddressMode)(Word0 & 7);
}
public AddressMode UnpackAddressV()
{
return (AddressMode)((Word0 >> 3) & 7);
}
public AddressMode UnpackAddressP()
{
return (AddressMode)((Word0 >> 6) & 7);
}
public CompareMode UnpackCompareMode()
{
return (CompareMode)((Word0 >> 9) & 1);
}
public CompareOp UnpackCompareOp()
{
return (CompareOp)(((Word0 >> 10) & 7) + 1);
}
public float UnpackMaxAnisotropy()
{
return _maxAnisotropyLut[(Word0 >> 20) & 7];
}
public MagFilter UnpackMagFilter()
{
return (MagFilter)(Word1 & 3);
}
public MinFilter UnpackMinFilter()
{
int minFilter = (int)(Word1 >> 4) & 3;
int mipFilter = (int)(Word1 >> 6) & 3;
return (MinFilter)(minFilter + (mipFilter - 1) * 2);
}
public ReductionFilter UnpackReductionFilter()
{
return (ReductionFilter)((Word1 >> 10) & 3);
}
public float UnpackMipLodBias()
{
int fixedValue = (int)(Word1 >> 12) & 0x1fff;
fixedValue = (fixedValue << 19) >> 19;
return fixedValue * Frac8ToF32;
}
public float UnpackLodSnap()
{
return _f5ToF32ConversionLut[(Word1 >> 26) & 0x1f];
}
public float UnpackMinLod()
{
return (Word2 & 0xfff) * Frac8ToF32;
}
public float UnpackMaxLod()
{
return ((Word2 >> 12) & 0xfff) * Frac8ToF32;
}
}
}

View file

@ -0,0 +1,61 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Image
{
class SamplerPool : Pool<Sampler>
{
public SamplerPool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
public override Sampler Get(int id)
{
if ((uint)id >= Items.Length)
{
return null;
}
SynchronizeMemory();
Sampler sampler = Items[id];
if (sampler == null)
{
ulong address = Address + (ulong)(uint)id * DescriptorSize;
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
SamplerDescriptor descriptor = MemoryMarshal.Cast<byte, SamplerDescriptor>(data)[0];
sampler = new Sampler(Context, descriptor);
Items[id] = sampler;
}
return sampler;
}
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)
{
sampler.Dispose();
Items[id] = null;
}
}
}
protected override void Delete(Sampler item)
{
item?.Dispose();
}
}
}

View file

@ -0,0 +1,719 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Texture;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
using Ryujinx.Graphics.Texture.Astc;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image
{
class Texture : IRange<Texture>
{
private GpuContext _context;
private TextureInfo _info;
private SizeInfo _sizeInfo;
public Format Format => _info.FormatInfo.Format;
public TextureInfo Info => _info;
private int _depth;
private int _layers;
private int _firstLayer;
private int _firstLevel;
private bool _hasData;
private ITexture _arrayViewTexture;
private Target _arrayViewTarget;
private Texture _viewStorage;
private List<Texture> _views;
public ITexture HostTexture { get; private set; }
public LinkedListNode<Texture> CacheNode { get; set; }
public bool Modified { get; set; }
public ulong Address => _info.Address;
public ulong EndAddress => _info.Address + Size;
public ulong Size => (ulong)_sizeInfo.TotalSize;
private int _referenceCount;
private int _sequenceNumber;
private Texture(
GpuContext context,
TextureInfo info,
SizeInfo sizeInfo,
int firstLayer,
int firstLevel)
{
InitializeTexture(context, info, sizeInfo);
_firstLayer = firstLayer;
_firstLevel = firstLevel;
_hasData = true;
}
public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
{
InitializeTexture(context, info, sizeInfo);
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, context.Capabilities);
HostTexture = _context.Renderer.CreateTexture(createInfo);
}
private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
{
_context = context;
_sizeInfo = sizeInfo;
SetInfo(info);
_viewStorage = this;
_views = new List<Texture>();
}
public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, int firstLayer, int firstLevel)
{
Texture texture = new Texture(
_context,
info,
sizeInfo,
_firstLayer + firstLayer,
_firstLevel + firstLevel);
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, _context.Capabilities);
texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel);
_viewStorage.AddView(texture);
return texture;
}
private void AddView(Texture texture)
{
_views.Add(texture);
texture._viewStorage = this;
}
private void RemoveView(Texture texture)
{
_views.Remove(texture);
texture._viewStorage = null;
}
public void ChangeSize(int width, int height, int depthOrLayers)
{
width <<= _firstLevel;
height <<= _firstLevel;
if (_info.Target == Target.Texture3D)
{
depthOrLayers <<= _firstLevel;
}
else
{
depthOrLayers = _viewStorage._info.DepthOrLayers;
}
_viewStorage.RecreateStorageOrView(width, height, depthOrLayers);
foreach (Texture view in _viewStorage._views)
{
int viewWidth = Math.Max(1, width >> view._firstLevel);
int viewHeight = Math.Max(1, height >> view._firstLevel);
int viewDepthOrLayers;
if (view._info.Target == Target.Texture3D)
{
viewDepthOrLayers = Math.Max(1, depthOrLayers >> view._firstLevel);
}
else
{
viewDepthOrLayers = view._info.DepthOrLayers;
}
view.RecreateStorageOrView(viewWidth, viewHeight, viewDepthOrLayers);
}
}
private void RecreateStorageOrView(int width, int height, int depthOrLayers)
{
SetInfo(new TextureInfo(
_info.Address,
width,
height,
depthOrLayers,
_info.Levels,
_info.SamplesInX,
_info.SamplesInY,
_info.Stride,
_info.IsLinear,
_info.GobBlocksInY,
_info.GobBlocksInZ,
_info.GobBlocksInTileX,
_info.Target,
_info.FormatInfo,
_info.DepthStencilMode,
_info.SwizzleR,
_info.SwizzleG,
_info.SwizzleB,
_info.SwizzleA));
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(_info, _context.Capabilities);
if (_viewStorage != this)
{
ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, _firstLayer, _firstLevel));
}
else
{
ITexture newStorage = _context.Renderer.CreateTexture(createInfo);
HostTexture.CopyTo(newStorage);
ReplaceStorage(newStorage);
}
}
public void SynchronizeMemory()
{
if (_sequenceNumber == _context.SequenceNumber && _hasData)
{
return;
}
_sequenceNumber = _context.SequenceNumber;
bool modified = _context.PhysicalMemory.GetModifiedRanges(Address, Size).Length != 0;
if (!modified && _hasData)
{
return;
}
ulong pageSize = (uint)_context.PhysicalMemory.GetPageSize();
ulong pageMask = pageSize - 1;
ulong rangeAddress = Address & ~pageMask;
ulong rangeSize = (EndAddress - Address + pageMask) & ~pageMask;
_context.Methods.InvalidateRange(rangeAddress, rangeSize);
Span<byte> data = _context.PhysicalMemory.Read(Address, Size);
if (_info.IsLinear)
{
data = LayoutConverter.ConvertLinearStridedToLinear(
_info.Width,
_info.Height,
_info.FormatInfo.BlockWidth,
_info.FormatInfo.BlockHeight,
_info.Stride,
_info.FormatInfo.BytesPerPixel,
data);
}
else
{
data = LayoutConverter.ConvertBlockLinearToLinear(
_info.Width,
_info.Height,
_depth,
_info.Levels,
_layers,
_info.FormatInfo.BlockWidth,
_info.FormatInfo.BlockHeight,
_info.FormatInfo.BytesPerPixel,
_info.GobBlocksInY,
_info.GobBlocksInZ,
_info.GobBlocksInTileX,
_sizeInfo,
data);
}
if (!_context.Capabilities.SupportsAstcCompression && _info.FormatInfo.Format.IsAstc())
{
int blockWidth = _info.FormatInfo.BlockWidth;
int blockHeight = _info.FormatInfo.BlockHeight;
data = AstcDecoder.DecodeToRgba8(
data,
blockWidth,
blockHeight,
1,
_info.Width,
_info.Height,
_depth);
}
HostTexture.SetData(data);
_hasData = true;
}
public void Flush()
{
byte[] data = HostTexture.GetData(0);
_context.PhysicalMemory.Write(Address, data);
}
public bool IsPerfectMatch(TextureInfo info, TextureSearchFlags flags)
{
if (!FormatMatches(info, (flags & TextureSearchFlags.Strict) != 0))
{
return false;
}
if (!LayoutMatches(info))
{
return false;
}
if (!SizeMatches(info, (flags & TextureSearchFlags.Strict) == 0))
{
return false;
}
if ((flags & TextureSearchFlags.Sampler) != 0)
{
if (!SamplerParamsMatches(info))
{
return false;
}
}
if ((flags & TextureSearchFlags.IgnoreMs) != 0)
{
bool msTargetCompatible = _info.Target == Target.Texture2DMultisample &&
info.Target == Target.Texture2D;
if (!msTargetCompatible && !TargetAndSamplesCompatible(info))
{
return false;
}
}
else if (!TargetAndSamplesCompatible(info))
{
return false;
}
return _info.Address == info.Address && _info.Levels == info.Levels;
}
private bool FormatMatches(TextureInfo info, bool strict)
{
// D32F and R32F texture have the same representation internally,
// however the R32F format is used to sample from depth textures.
if (_info.FormatInfo.Format == Format.D32Float &&
info.FormatInfo.Format == Format.R32Float && !strict)
{
return true;
}
if (_info.FormatInfo.Format == Format.R8G8B8A8Srgb &&
info.FormatInfo.Format == Format.R8G8B8A8Unorm && !strict)
{
return true;
}
if (_info.FormatInfo.Format == Format.R8G8B8A8Unorm &&
info.FormatInfo.Format == Format.R8G8B8A8Srgb && !strict)
{
return true;
}
return _info.FormatInfo.Format == info.FormatInfo.Format;
}
private bool LayoutMatches(TextureInfo info)
{
if (_info.IsLinear != info.IsLinear)
{
return false;
}
// For linear textures, gob block sizes are ignored.
// For block linear textures, the stride is ignored.
if (info.IsLinear)
{
return _info.Stride == info.Stride;
}
else
{
return _info.GobBlocksInY == info.GobBlocksInY &&
_info.GobBlocksInZ == info.GobBlocksInZ;
}
}
public bool SizeMatches(TextureInfo info)
{
return SizeMatches(info, alignSizes: false);
}
public bool SizeMatches(TextureInfo info, int level)
{
return Math.Max(1, _info.Width >> level) == info.Width &&
Math.Max(1, _info.Height >> level) == info.Height &&
Math.Max(1, _info.GetDepth() >> level) == info.GetDepth();
}
private bool SizeMatches(TextureInfo info, bool alignSizes)
{
if (_info.GetLayers() != info.GetLayers())
{
return false;
}
if (alignSizes)
{
Size size0 = GetAlignedSize(_info);
Size size1 = GetAlignedSize(info);
return size0.Width == size1.Width &&
size0.Height == size1.Height &&
size0.Depth == size1.Depth;
}
else
{
return _info.Width == info.Width &&
_info.Height == info.Height &&
_info.GetDepth() == info.GetDepth();
}
}
private bool SamplerParamsMatches(TextureInfo info)
{
return _info.DepthStencilMode == info.DepthStencilMode &&
_info.SwizzleR == info.SwizzleR &&
_info.SwizzleG == info.SwizzleG &&
_info.SwizzleB == info.SwizzleB &&
_info.SwizzleA == info.SwizzleA;
}
private bool TargetAndSamplesCompatible(TextureInfo info)
{
return _info.Target == info.Target &&
_info.SamplesInX == info.SamplesInX &&
_info.SamplesInY == info.SamplesInY;
}
public bool IsViewCompatible(TextureInfo info, ulong size, out int firstLayer, out int firstLevel)
{
// Out of range.
if (info.Address < Address || info.Address + size > EndAddress)
{
firstLayer = 0;
firstLevel = 0;
return false;
}
int offset = (int)(info.Address - Address);
if (!_sizeInfo.FindView(offset, (int)size, out firstLayer, out firstLevel))
{
return false;
}
if (!ViewLayoutCompatible(info, firstLevel))
{
return false;
}
if (!ViewFormatCompatible(info))
{
return false;
}
if (!ViewSizeMatches(info, firstLevel))
{
return false;
}
if (!ViewTargetCompatible(info))
{
return false;
}
return _info.SamplesInX == info.SamplesInX &&
_info.SamplesInY == info.SamplesInY;
}
private bool ViewLayoutCompatible(TextureInfo info, int level)
{
if (_info.IsLinear != info.IsLinear)
{
return false;
}
// For linear textures, gob block sizes are ignored.
// For block linear textures, the stride is ignored.
if (info.IsLinear)
{
int width = Math.Max(1, _info.Width >> level);
int stride = width * _info.FormatInfo.BytesPerPixel;
stride = BitUtils.AlignUp(stride, 32);
return stride == info.Stride;
}
else
{
int height = Math.Max(1, _info.Height >> level);
int depth = Math.Max(1, _info.GetDepth() >> level);
(int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
height,
depth,
_info.FormatInfo.BlockHeight,
_info.GobBlocksInY,
_info.GobBlocksInZ);
return gobBlocksInY == info.GobBlocksInY &&
gobBlocksInZ == info.GobBlocksInZ;
}
}
private bool ViewFormatCompatible(TextureInfo info)
{
return TextureCompatibility.FormatCompatible(_info.FormatInfo, info.FormatInfo);
}
private bool ViewSizeMatches(TextureInfo info, int level)
{
Size size = GetAlignedSize(_info, level);
Size otherSize = GetAlignedSize(info);
return size.Width == otherSize.Width &&
size.Height == otherSize.Height &&
size.Depth == otherSize.Depth;
}
private bool ViewTargetCompatible(TextureInfo info)
{
switch (_info.Target)
{
case Target.Texture1D:
case Target.Texture1DArray:
return info.Target == Target.Texture1D ||
info.Target == Target.Texture1DArray;
case Target.Texture2D:
return info.Target == Target.Texture2D ||
info.Target == Target.Texture2DArray;
case Target.Texture2DArray:
case Target.Cubemap:
case Target.CubemapArray:
return info.Target == Target.Texture2D ||
info.Target == Target.Texture2DArray ||
info.Target == Target.Cubemap ||
info.Target == Target.CubemapArray;
case Target.Texture2DMultisample:
case Target.Texture2DMultisampleArray:
return info.Target == Target.Texture2DMultisample ||
info.Target == Target.Texture2DMultisampleArray;
case Target.Texture3D:
return info.Target == Target.Texture3D;
}
return false;
}
private static Size GetAlignedSize(TextureInfo info, int level = 0)
{
int width = Math.Max(1, info.Width >> level);
int height = Math.Max(1, info.Height >> level);
if (info.IsLinear)
{
return SizeCalculator.GetLinearAlignedSize(
width,
height,
info.FormatInfo.BlockWidth,
info.FormatInfo.BlockHeight,
info.FormatInfo.BytesPerPixel);
}
else
{
int depth = Math.Max(1, info.GetDepth() >> level);
(int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
height,
depth,
info.FormatInfo.BlockHeight,
info.GobBlocksInY,
info.GobBlocksInZ);
return SizeCalculator.GetBlockLinearAlignedSize(
width,
height,
depth,
info.FormatInfo.BlockWidth,
info.FormatInfo.BlockHeight,
info.FormatInfo.BytesPerPixel,
gobBlocksInY,
gobBlocksInZ,
info.GobBlocksInTileX);
}
}
public ITexture GetTargetTexture(Target target)
{
if (target == _info.Target)
{
return HostTexture;
}
if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
{
TextureCreateInfo createInfo = new TextureCreateInfo(
_info.Width,
_info.Height,
target == Target.CubemapArray ? 6 : 1,
_info.Levels,
_info.Samples,
_info.FormatInfo.BlockWidth,
_info.FormatInfo.BlockHeight,
_info.FormatInfo.BytesPerPixel,
_info.FormatInfo.Format,
_info.DepthStencilMode,
target,
_info.SwizzleR,
_info.SwizzleG,
_info.SwizzleB,
_info.SwizzleA);
ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0);
_arrayViewTexture = viewTexture;
_arrayViewTarget = target;
return viewTexture;
}
else if (_arrayViewTarget == target)
{
return _arrayViewTexture;
}
return null;
}
private bool IsSameDimensionsTarget(Target target)
{
switch (_info.Target)
{
case Target.Texture1D:
case Target.Texture1DArray:
return target == Target.Texture1D ||
target == Target.Texture1DArray;
case Target.Texture2D:
case Target.Texture2DArray:
return target == Target.Texture2D ||
target == Target.Texture2DArray;
case Target.Cubemap:
case Target.CubemapArray:
return target == Target.Cubemap ||
target == Target.CubemapArray;
case Target.Texture2DMultisample:
case Target.Texture2DMultisampleArray:
return target == Target.Texture2DMultisample ||
target == Target.Texture2DMultisampleArray;
case Target.Texture3D:
return target == Target.Texture3D;
}
return false;
}
public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture)
{
ReplaceStorage(hostTexture);
parent._viewStorage.AddView(this);
SetInfo(info);
}
private void SetInfo(TextureInfo info)
{
_info = info;
_depth = info.GetDepth();
_layers = info.GetLayers();
}
private void ReplaceStorage(ITexture hostTexture)
{
DisposeTextures();
HostTexture = hostTexture;
}
public bool OverlapsWith(ulong address, ulong size)
{
return Address < address + size && address < EndAddress;
}
public void Invalidate()
{
// _hasData = false;
}
public void IncrementReferenceCount()
{
_referenceCount++;
}
public void DecrementReferenceCount()
{
if (--_referenceCount == 0)
{
if (_viewStorage != this)
{
_viewStorage.RemoveView(this);
}
_context.Methods.TextureManager.RemoveTextureFromCache(this);
DisposeTextures();
}
}
private void DisposeTextures()
{
HostTexture.Dispose();
_arrayViewTexture?.Dispose();
_arrayViewTexture = null;
}
}
}

View file

@ -0,0 +1,17 @@
using Ryujinx.Graphics.GAL.Texture;
namespace Ryujinx.Graphics.Gpu.Image
{
struct TextureBindingInfo
{
public Target Target { get; }
public int Handle { get; }
public TextureBindingInfo(Target target, int handle)
{
Target = target;
Handle = handle;
}
}
}

View file

@ -0,0 +1,95 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.Image
{
static class TextureCompatibility
{
private enum FormatClass
{
Unclassified,
BCn64,
BCn128,
Bc1Rgb,
Bc1Rgba,
Bc2,
Bc3,
Bc4,
Bc5,
Bc6,
Bc7
}
public static bool FormatCompatible(FormatInfo lhs, FormatInfo rhs)
{
if (IsDsFormat(lhs.Format) || IsDsFormat(rhs.Format))
{
return lhs.Format == rhs.Format;
}
if (lhs.Format.IsAstc() || rhs.Format.IsAstc())
{
return lhs.Format == rhs.Format;
}
if (lhs.IsCompressed && rhs.IsCompressed)
{
FormatClass lhsClass = GetFormatClass(lhs.Format);
FormatClass rhsClass = GetFormatClass(rhs.Format);
return lhsClass == rhsClass;
}
else
{
return lhs.BytesPerPixel == rhs.BytesPerPixel;
}
}
private static FormatClass GetFormatClass(Format format)
{
switch (format)
{
case Format.Bc1RgbSrgb:
case Format.Bc1RgbUnorm:
return FormatClass.Bc1Rgb;
case Format.Bc1RgbaSrgb:
case Format.Bc1RgbaUnorm:
return FormatClass.Bc1Rgba;
case Format.Bc2Srgb:
case Format.Bc2Unorm:
return FormatClass.Bc2;
case Format.Bc3Srgb:
case Format.Bc3Unorm:
return FormatClass.Bc3;
case Format.Bc4Snorm:
case Format.Bc4Unorm:
return FormatClass.Bc4;
case Format.Bc5Snorm:
case Format.Bc5Unorm:
return FormatClass.Bc5;
case Format.Bc6HSfloat:
case Format.Bc6HUfloat:
return FormatClass.Bc6;
case Format.Bc7Srgb:
case Format.Bc7Unorm:
return FormatClass.Bc7;
}
return FormatClass.Unclassified;
}
private static bool IsDsFormat(Format format)
{
switch (format)
{
case Format.D16Unorm:
case Format.D24X8Unorm:
case Format.D24UnormS8Uint:
case Format.D32Float:
case Format.D32FloatS8Uint:
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,35 @@
using Ryujinx.Graphics.GAL.Texture;
namespace Ryujinx.Graphics.Gpu.Image
{
enum TextureComponent
{
Zero = 0,
Red = 2,
Green = 3,
Blue = 4,
Alpha = 5,
OneSI = 6,
OneF = 7
}
static class TextureComponentConverter
{
public static SwizzleComponent Convert(this TextureComponent component)
{
switch (component)
{
case TextureComponent.Zero: return SwizzleComponent.Zero;
case TextureComponent.Red: return SwizzleComponent.Red;
case TextureComponent.Green: return SwizzleComponent.Green;
case TextureComponent.Blue: return SwizzleComponent.Blue;
case TextureComponent.Alpha: return SwizzleComponent.Alpha;
case TextureComponent.OneSI:
case TextureComponent.OneF:
return SwizzleComponent.One;
}
return SwizzleComponent.Zero;
}
}
}

View file

@ -0,0 +1,119 @@
namespace Ryujinx.Graphics.Gpu.Image
{
struct TextureDescriptor
{
public uint Word0;
public uint Word1;
public uint Word2;
public uint Word3;
public uint Word4;
public uint Word5;
public uint Word6;
public uint Word7;
public uint UnpackFormat()
{
return Word0 & 0x8007ffff;
}
public TextureComponent UnpackSwizzleR()
{
return(TextureComponent)((Word0 >> 19) & 7);
}
public TextureComponent UnpackSwizzleG()
{
return(TextureComponent)((Word0 >> 22) & 7);
}
public TextureComponent UnpackSwizzleB()
{
return(TextureComponent)((Word0 >> 25) & 7);
}
public TextureComponent UnpackSwizzleA()
{
return(TextureComponent)((Word0 >> 28) & 7);
}
public ulong UnpackAddress()
{
return Word1 | ((ulong)(Word2 & 0xffff) << 32);
}
public TextureDescriptorType UnpackTextureDescriptorType()
{
return (TextureDescriptorType)((Word2 >> 21) & 7);
}
public int UnpackStride()
{
return (int)(Word3 & 0xffff) << 5;
}
public int UnpackGobBlocksInX()
{
return 1 << (int)(Word3 & 7);
}
public int UnpackGobBlocksInY()
{
return 1 << (int)((Word3 >> 3) & 7);
}
public int UnpackGobBlocksInZ()
{
return 1 << (int)((Word3 >> 6) & 7);
}
public int UnpackGobBlocksInTileX()
{
return 1 << (int)((Word3 >> 10) & 7);
}
public int UnpackLevels()
{
return (int)(Word3 >> 28) + 1;
}
public int UnpackWidth()
{
return (int)(Word4 & 0xffff) + 1;
}
public bool UnpackSrgb()
{
return (Word4 & (1 << 22)) != 0;
}
public TextureTarget UnpackTextureTarget()
{
return (TextureTarget)((Word4 >> 23) & 0xf);
}
public int UnpackHeight()
{
return (int)(Word5 & 0xffff) + 1;
}
public int UnpackDepth()
{
return (int)((Word5 >> 16) & 0x3fff) + 1;
}
public int UnpackBaseLevel()
{
return (int)(Word7 & 0xf);
}
public int UnpackMaxLevelInclusive()
{
return (int)((Word7 >> 4) & 0xf);
}
public TextureMsaaMode UnpackTextureMsaaMode()
{
return (TextureMsaaMode)((Word7 >> 8) & 0xf);
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Graphics.Gpu.Image
{
enum TextureDescriptorType
{
Buffer,
LinearColorKey,
Linear,
BlockLinear,
BlockLinearColorKey
}
}

View file

@ -0,0 +1,101 @@
using Ryujinx.Graphics.GAL.Texture;
namespace Ryujinx.Graphics.Gpu.Image
{
struct TextureInfo
{
public ulong Address { get; }
public int Width { get; }
public int Height { get; }
public int DepthOrLayers { get; }
public int Levels { get; }
public int SamplesInX { get; }
public int SamplesInY { get; }
public int Stride { get; }
public bool IsLinear { get; }
public int GobBlocksInY { get; }
public int GobBlocksInZ { get; }
public int GobBlocksInTileX { get; }
public int Samples => SamplesInX * SamplesInY;
public Target Target { get; }
public FormatInfo FormatInfo { get; }
public DepthStencilMode DepthStencilMode { get; }
public SwizzleComponent SwizzleR { get; }
public SwizzleComponent SwizzleG { get; }
public SwizzleComponent SwizzleB { get; }
public SwizzleComponent SwizzleA { get; }
public TextureInfo(
ulong address,
int width,
int height,
int depthOrLayers,
int levels,
int samplesInX,
int samplesInY,
int stride,
bool isLinear,
int gobBlocksInY,
int gobBlocksInZ,
int gobBlocksInTileX,
Target target,
FormatInfo formatInfo,
DepthStencilMode depthStencilMode = DepthStencilMode.Depth,
SwizzleComponent swizzleR = SwizzleComponent.Red,
SwizzleComponent swizzleG = SwizzleComponent.Green,
SwizzleComponent swizzleB = SwizzleComponent.Blue,
SwizzleComponent swizzleA = SwizzleComponent.Alpha)
{
Address = address;
Width = width;
Height = height;
DepthOrLayers = depthOrLayers;
Levels = levels;
SamplesInX = samplesInX;
SamplesInY = samplesInY;
Stride = stride;
IsLinear = isLinear;
GobBlocksInY = gobBlocksInY;
GobBlocksInZ = gobBlocksInZ;
GobBlocksInTileX = gobBlocksInTileX;
Target = target;
FormatInfo = formatInfo;
DepthStencilMode = depthStencilMode;
SwizzleR = swizzleR;
SwizzleG = swizzleG;
SwizzleB = swizzleB;
SwizzleA = swizzleA;
}
public int GetDepth()
{
return Target == Target.Texture3D ? DepthOrLayers : 1;
}
public int GetLayers()
{
if (Target == Target.Texture2DArray || Target == Target.Texture2DMultisampleArray)
{
return DepthOrLayers;
}
else if (Target == Target.CubemapArray)
{
return DepthOrLayers * 6;
}
else if (Target == Target.Cubemap)
{
return 6;
}
else
{
return 1;
}
}
}
}

View file

@ -0,0 +1,669 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Texture;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gpu.Image
{
class TextureManager
{
private GpuContext _context;
private BufferManager _bufferManager;
private SamplerPool _samplerPool;
private ulong _texturePoolAddress;
private int _texturePoolMaximumId;
private TexturePoolCache _texturePoolCache;
private Texture[] _rtColors;
private Texture _rtColor3D;
private Texture _rtDepthStencil;
private ITexture[] _rtHostColors;
private ITexture _rtHostDs;
private RangeList<Texture> _textures;
private AutoDeleteCache _cache;
private TextureBindingInfo[][] _bindings;
private struct TextureStatePerStage
{
public ITexture Texture;
public ISampler Sampler;
}
private TextureStatePerStage[][] _textureState;
private int _textureBufferIndex;
public TextureManager(GpuContext context, BufferManager bufferManager)
{
_context = context;
_bufferManager = bufferManager;
_texturePoolCache = new TexturePoolCache(context, this);
_rtColors = new Texture[Constants.TotalRenderTargets];
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
_textures = new RangeList<Texture>();
_cache = new AutoDeleteCache();
_bindings = new TextureBindingInfo[Constants.TotalShaderStages][];
_textureState = new TextureStatePerStage[Constants.TotalShaderStages][];
}
public void BindTextures(int stage, TextureBindingInfo[] bindings)
{
_bindings[stage] = bindings;
_textureState[stage] = new TextureStatePerStage[bindings.Length];
}
public void SetTextureBufferIndex(int index)
{
_textureBufferIndex = index;
}
public void SetSamplerPool(ulong gpuVa, int maximumId)
{
ulong address = _context.MemoryManager.Translate(gpuVa);
if (_samplerPool != null)
{
if (_samplerPool.Address == address)
{
return;
}
_samplerPool.Dispose();
}
_samplerPool = new SamplerPool(_context, address, maximumId);
}
public void SetTexturePool(ulong gpuVa, int maximumId)
{
ulong address = _context.MemoryManager.Translate(gpuVa);
_texturePoolAddress = address;
_texturePoolMaximumId = maximumId;
}
public void SetRenderTargetColor(int index, Texture color)
{
_rtColors[index] = color;
_rtColor3D = null;
}
public void SetRenderTargetColor3D(Texture color)
{
_rtColor3D = color;
}
public void SetRenderTargetDepthStencil(Texture depthStencil)
{
_rtDepthStencil = depthStencil;
}
public void CommitBindings()
{
UpdateTextures();
UpdateRenderTargets();
}
private void UpdateTextures()
{
TexturePool texturePool = _texturePoolCache.FindOrCreate(
_texturePoolAddress,
_texturePoolMaximumId);
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
{
int stageIndex = (int)stage - 1;
if (_bindings[stageIndex] == null)
{
continue;
}
for (int index = 0; index < _bindings[stageIndex].Length; index++)
{
TextureBindingInfo binding = _bindings[stageIndex][index];
int packedId = ReadPackedId(stageIndex, binding.Handle);
int textureId = (packedId >> 0) & 0xfffff;
int samplerId = (packedId >> 20) & 0xfff;
Texture texture = texturePool.Get(textureId);
ITexture hostTexture = texture?.GetTargetTexture(binding.Target);
if (_textureState[stageIndex][index].Texture != hostTexture)
{
_textureState[stageIndex][index].Texture = hostTexture;
_context.Renderer.GraphicsPipeline.BindTexture(index, stage, hostTexture);
}
Sampler sampler = _samplerPool.Get(samplerId);
ISampler hostSampler = sampler?.HostSampler;
if (_textureState[stageIndex][index].Sampler != hostSampler)
{
_textureState[stageIndex][index].Sampler = hostSampler;
_context.Renderer.GraphicsPipeline.BindSampler(index, stage, hostSampler);
}
}
}
}
private void UpdateRenderTargets()
{
bool anyChanged = false;
if (_rtHostDs != _rtDepthStencil?.HostTexture)
{
_rtHostDs = _rtDepthStencil?.HostTexture;
anyChanged = true;
}
if (_rtColor3D == null)
{
for (int index = 0; index < _rtColors.Length; index++)
{
ITexture hostTexture = _rtColors[index]?.HostTexture;
if (_rtHostColors[index] != hostTexture)
{
_rtHostColors[index] = hostTexture;
anyChanged = true;
}
}
if (anyChanged)
{
_context.Renderer.GraphicsPipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
}
}
else
{
if (_rtHostColors[0] != _rtColor3D.HostTexture)
{
_rtHostColors[0] = _rtColor3D.HostTexture;
anyChanged = true;
}
if (anyChanged)
{
_context.Renderer.GraphicsPipeline.SetRenderTargets(_rtColor3D.HostTexture, _rtHostDs);
}
}
}
private int ReadPackedId(int stage, int wordOffset)
{
ulong address = _bufferManager.GetGraphicsUniformBufferAddress(stage, _textureBufferIndex);
address += (uint)wordOffset * 4;
return BitConverter.ToInt32(_context.PhysicalMemory.Read(address, 4));
}
public Texture FindOrCreateTexture(CopyTexture copyTexture)
{
ulong address = _context.MemoryManager.Translate(copyTexture.Address.Pack());
if (address == MemoryManager.BadAddress)
{
return null;
}
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
FormatInfo formatInfo = copyTexture.Format.Convert();
TextureInfo info = new TextureInfo(
address,
copyTexture.Width,
copyTexture.Height,
copyTexture.Depth,
1,
1,
1,
copyTexture.Stride,
copyTexture.LinearLayout,
gobBlocksInY,
gobBlocksInZ,
1,
Target.Texture2D,
formatInfo);
Texture texture = FindOrCreateTexture(info, TextureSearchFlags.IgnoreMs);
texture.SynchronizeMemory();
return texture;
}
public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY)
{
ulong address = _context.MemoryManager.Translate(colorState.Address.Pack());
if (address == MemoryManager.BadAddress)
{
return null;
}
bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
int gobBlocksInY = colorState.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = colorState.MemoryLayout.UnpackGobBlocksInZ();
Target target;
if (colorState.MemoryLayout.UnpackIsTarget3D())
{
target = Target.Texture3D;
}
else if ((samplesInX | samplesInY) != 1)
{
target = colorState.Depth > 1
? Target.Texture2DMultisampleArray
: Target.Texture2DMultisample;
}
else
{
target = colorState.Depth > 1
? Target.Texture2DArray
: Target.Texture2D;
}
FormatInfo formatInfo = colorState.Format.Convert();
int width, stride;
// For linear textures, the width value is actually the stride.
// We can easily get the width by dividing the stride by the bpp,
// since the stride is the total number of bytes occupied by a
// line. The stride should also meet alignment constraints however,
// so the width we get here is the aligned width.
if (isLinear)
{
width = colorState.WidthOrStride / formatInfo.BytesPerPixel;
stride = colorState.WidthOrStride;
}
else
{
width = colorState.WidthOrStride;
stride = 0;
}
TextureInfo info = new TextureInfo(
address,
width,
colorState.Height,
colorState.Depth,
1,
samplesInX,
samplesInY,
stride,
isLinear,
gobBlocksInY,
gobBlocksInZ,
1,
target,
formatInfo);
Texture texture = FindOrCreateTexture(info);
texture.SynchronizeMemory();
return texture;
}
public Texture FindOrCreateTexture(RtDepthStencilState dsState, Size3D size, int samplesInX, int samplesInY)
{
ulong address = _context.MemoryManager.Translate(dsState.Address.Pack());
if (address == MemoryManager.BadAddress)
{
return null;
}
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
Target target = (samplesInX | samplesInY) != 1
? Target.Texture2DMultisample
: Target.Texture2D;
FormatInfo formatInfo = dsState.Format.Convert();
TextureInfo info = new TextureInfo(
address,
size.Width,
size.Height,
size.Depth,
1,
samplesInX,
samplesInY,
0,
false,
gobBlocksInY,
gobBlocksInZ,
1,
target,
formatInfo);
Texture texture = FindOrCreateTexture(info);
texture.SynchronizeMemory();
return texture;
}
public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None)
{
bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0;
// Try to find a perfect texture match, with the same address and parameters.
Texture[] sameAddressOverlaps = _textures.FindOverlaps(info.Address);
foreach (Texture overlap in sameAddressOverlaps)
{
if (overlap.IsPerfectMatch(info, flags))
{
if (!isSamplerTexture)
{
// If not a sampler texture, it is managed by the auto delete
// cache, ensure that it is on the "top" of the list to avoid
// deletion.
_cache.Lift(overlap);
}
else if (!overlap.SizeMatches(info))
{
// If this is used for sampling, the size must match,
// otherwise the shader would sample garbage data.
// To fix that, we create a new texture with the correct
// size, and copy the data from the old one to the new one.
overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
}
return overlap;
}
}
// Calculate texture sizes, used to find all overlapping textures.
SizeInfo sizeInfo;
if (info.IsLinear)
{
sizeInfo = SizeCalculator.GetLinearTextureSize(
info.Stride,
info.Height,
info.FormatInfo.BlockHeight);
}
else
{
sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
info.Width,
info.Height,
info.GetDepth(),
info.Levels,
info.GetLayers(),
info.FormatInfo.BlockWidth,
info.FormatInfo.BlockHeight,
info.FormatInfo.BytesPerPixel,
info.GobBlocksInY,
info.GobBlocksInZ,
info.GobBlocksInTileX);
}
// Find view compatible matches.
ulong size = (ulong)sizeInfo.TotalSize;
Texture[] overlaps = _textures.FindOverlaps(info.Address, size);
Texture texture = null;
foreach (Texture overlap in overlaps)
{
if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel))
{
if (!isSamplerTexture)
{
info = AdjustSizes(overlap, info, firstLevel);
}
texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);
// The size only matters (and is only really reliable) when the
// texture is used on a sampler, because otherwise the size will be
// aligned.
if (!overlap.SizeMatches(info, firstLevel) && isSamplerTexture)
{
texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
}
break;
}
}
// No match, create a new texture.
if (texture == null)
{
texture = new Texture(_context, info, sizeInfo);
// We need to synchronize before copying the old view data to the texture,
// otherwise the copied data would be overwritten by a future synchronization.
texture.SynchronizeMemory();
foreach (Texture overlap in overlaps)
{
if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel))
{
TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel);
TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities);
ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel);
overlap.HostTexture.CopyTo(newView);
overlap.ReplaceView(texture, overlapInfo, newView);
}
}
}
// Sampler textures are managed by the texture pool, all other textures
// are managed by the auto delete cache.
if (!isSamplerTexture)
{
_cache.Add(texture);
}
_textures.Add(texture);
return texture;
}
private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
{
// When the texture is used as view of another texture, we must
// ensure that the sizes are valid, otherwise data uploads would fail
// (and the size wouldn't match the real size used on the host API).
// Given a parent texture from where the view is created, we have the
// following rules:
// - The view size must be equal to the parent size, divided by (2 ^ l),
// where l is the first mipmap level of the view. The division result must
// be rounded down, and the result must be clamped to 1.
// - If the parent format is compressed, and the view format isn't, the
// view size is calculated as above, but the width and height of the
// view must be also divided by the compressed format block width and height.
// - If the parent format is not compressed, and the view is, the view
// size is calculated as described on the first point, but the width and height
// of the view must be also multiplied by the block width and height.
int width = Math.Max(1, parent.Info.Width >> firstLevel);
int height = Math.Max(1, parent.Info.Height >> firstLevel);
if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed)
{
width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
}
else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed)
{
width *= info.FormatInfo.BlockWidth;
height *= info.FormatInfo.BlockHeight;
}
int depthOrLayers;
if (info.Target == Target.Texture3D)
{
depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
}
else
{
depthOrLayers = info.DepthOrLayers;
}
return new TextureInfo(
info.Address,
width,
height,
depthOrLayers,
info.Levels,
info.SamplesInX,
info.SamplesInY,
info.Stride,
info.IsLinear,
info.GobBlocksInY,
info.GobBlocksInZ,
info.GobBlocksInTileX,
info.Target,
info.FormatInfo,
info.DepthStencilMode,
info.SwizzleR,
info.SwizzleG,
info.SwizzleB,
info.SwizzleA);
}
public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps)
{
FormatInfo formatInfo = info.FormatInfo;
if (!caps.SupportsAstcCompression)
{
if (formatInfo.Format.IsAstcUnorm())
{
formatInfo = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
}
else if (formatInfo.Format.IsAstcSrgb())
{
formatInfo = new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4);
}
}
int width = info.Width / info.SamplesInX;
int height = info.Height / info.SamplesInY;
int depth = info.GetDepth() * info.GetLayers();
return new TextureCreateInfo(
width,
height,
depth,
info.Levels,
info.Samples,
formatInfo.BlockWidth,
formatInfo.BlockHeight,
formatInfo.BytesPerPixel,
formatInfo.Format,
info.DepthStencilMode,
info.Target,
info.SwizzleR,
info.SwizzleG,
info.SwizzleB,
info.SwizzleA);
}
public Texture Find2(ulong address)
{
Texture[] ts = _textures.FindOverlaps(address, 1);
if (ts.Length == 2)
{
return ts[1];
}
if (ts.Length == 0)
{
ts = _textures.FindOverlaps(address - 1, 2);
}
if (ts.Length == 0)
{
return null;
}
return ts[0];
}
public void InvalidateRange(ulong address, ulong size)
{
Texture[] overlaps = _textures.FindOverlaps(address, size);
foreach (Texture overlap in overlaps)
{
overlap.Invalidate();
}
_samplerPool?.InvalidateRange(address, size);
_texturePoolCache.InvalidateRange(address, size);
}
public void Flush()
{
foreach (Texture texture in _cache)
{
if (texture.Info.IsLinear && texture.Modified)
{
texture.Flush();
texture.Modified = false;
}
}
}
public void RemoveTextureFromCache(Texture texture)
{
_textures.Remove(texture);
}
}
}

View file

@ -0,0 +1,53 @@
namespace Ryujinx.Graphics.Gpu.Image
{
enum TextureMsaaMode
{
Ms1x1 = 0,
Ms2x2 = 2,
Ms4x2 = 4,
Ms2x1 = 5,
Ms4x4 = 6
}
static class TextureMsaaModeConverter
{
public static int SamplesCount(this TextureMsaaMode msaaMode)
{
switch (msaaMode)
{
case TextureMsaaMode.Ms2x1: return 2;
case TextureMsaaMode.Ms2x2: return 4;
case TextureMsaaMode.Ms4x2: return 8;
case TextureMsaaMode.Ms4x4: return 16;
}
return 1;
}
public static int SamplesInX(this TextureMsaaMode msaaMode)
{
switch (msaaMode)
{
case TextureMsaaMode.Ms2x1: return 2;
case TextureMsaaMode.Ms2x2: return 2;
case TextureMsaaMode.Ms4x2: return 4;
case TextureMsaaMode.Ms4x4: return 4;
}
return 1;
}
public static int SamplesInY(this TextureMsaaMode msaaMode)
{
switch (msaaMode)
{
case TextureMsaaMode.Ms2x1: return 1;
case TextureMsaaMode.Ms2x2: return 2;
case TextureMsaaMode.Ms4x2: return 2;
case TextureMsaaMode.Ms4x4: return 4;
}
return 1;
}
}
}

View file

@ -0,0 +1,219 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Texture;
using Ryujinx.Graphics.Gpu.Memory;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Image
{
class TexturePool : Pool<Texture>
{
private TextureManager _textureManager;
public LinkedListNode<TexturePool> CacheNode { get; set; }
private struct TextureContainer
{
public Texture Texture0 { get; set; }
public Texture Texture1 { get; set; }
}
public TexturePool(
GpuContext context,
TextureManager textureManager,
ulong address,
int maximumId) : base(context, address, maximumId)
{
_textureManager = textureManager;
}
public override Texture Get(int id)
{
if ((uint)id >= Items.Length)
{
return null;
}
SynchronizeMemory();
Texture texture = Items[id];
if (texture == null)
{
ulong address = Address + (ulong)(uint)id * DescriptorSize;
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
TextureDescriptor descriptor = MemoryMarshal.Cast<byte, TextureDescriptor>(data)[0];
TextureInfo info = GetInfo(descriptor);
// Bad address. We can't add a texture with a invalid address
// to the cache.
if (info.Address == MemoryManager.BadAddress)
{
return null;
}
texture = _textureManager.FindOrCreateTexture(info, TextureSearchFlags.Sampler);
texture.IncrementReferenceCount();
Items[id] = texture;
}
else
{
// Memory is automatically synchronized on texture creation.
texture.SynchronizeMemory();
}
return texture;
}
protected override void InvalidateRangeImpl(ulong address, ulong size)
{
ulong endAddress = address + size;
for (; address < endAddress; address += DescriptorSize)
{
int id = (int)((address - Address) / DescriptorSize);
Texture texture = Items[id];
if (texture != null)
{
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
TextureDescriptor descriptor = MemoryMarshal.Cast<byte, TextureDescriptor>(data)[0];
// If the descriptors are the same, the texture is the same,
// we don't need to remove as it was not modified. Just continue.
if (texture.IsPerfectMatch(GetInfo(descriptor), TextureSearchFlags.Strict))
{
continue;
}
texture.DecrementReferenceCount();
Items[id] = null;
}
}
}
private TextureInfo GetInfo(TextureDescriptor descriptor)
{
ulong address = Context.MemoryManager.Translate(descriptor.UnpackAddress());
int width = descriptor.UnpackWidth();
int height = descriptor.UnpackHeight();
int depthOrLayers = descriptor.UnpackDepth();
int levels = descriptor.UnpackLevels();
TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode();
int samplesInX = msaaMode.SamplesInX();
int samplesInY = msaaMode.SamplesInY();
int stride = descriptor.UnpackStride();
TextureDescriptorType descriptorType = descriptor.UnpackTextureDescriptorType();
bool isLinear = descriptorType == TextureDescriptorType.Linear;
Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1);
uint format = descriptor.UnpackFormat();
bool srgb = descriptor.UnpackSrgb();
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
{
// TODO: Warning.
formatInfo = FormatInfo.Default;
}
int gobBlocksInY = descriptor.UnpackGobBlocksInY();
int gobBlocksInZ = descriptor.UnpackGobBlocksInZ();
int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX();
SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
SwizzleComponent swizzleA = descriptor.UnpackSwizzleA().Convert();
DepthStencilMode depthStencilMode = GetDepthStencilMode(
formatInfo.Format,
swizzleR,
swizzleG,
swizzleB,
swizzleA);
return new TextureInfo(
address,
width,
height,
depthOrLayers,
levels,
samplesInX,
samplesInY,
stride,
isLinear,
gobBlocksInY,
gobBlocksInZ,
gobBlocksInTileX,
target,
formatInfo,
depthStencilMode,
swizzleR,
swizzleG,
swizzleB,
swizzleA);
}
private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components)
{
// R = Depth, G = Stencil.
// On 24-bits depth formats, this is inverted (Stencil is R etc).
// NVN setup:
// For depth, A is set to 1.0f, the other components are set to Depth.
// For stencil, all components are set to Stencil.
SwizzleComponent component = components[0];
for (int index = 1; index < 4 && !IsRG(component); index++)
{
component = components[index];
}
if (!IsRG(component))
{
return DepthStencilMode.Depth;
}
if (format == Format.D24X8Unorm || format == Format.D24UnormS8Uint)
{
return component == SwizzleComponent.Red
? DepthStencilMode.Stencil
: DepthStencilMode.Depth;
}
else
{
return component == SwizzleComponent.Red
? DepthStencilMode.Depth
: DepthStencilMode.Stencil;
}
}
private static bool IsRG(SwizzleComponent component)
{
return component == SwizzleComponent.Red ||
component == SwizzleComponent.Green;
}
protected override void Delete(Texture item)
{
item?.DecrementReferenceCount();
}
}
}

View file

@ -0,0 +1,73 @@
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image
{
class TexturePoolCache
{
private const int MaxCapacity = 4;
private GpuContext _context;
private TextureManager _textureManager;
private LinkedList<TexturePool> _pools;
public TexturePoolCache(GpuContext context, TextureManager textureManager)
{
_context = context;
_textureManager = textureManager;
_pools = new LinkedList<TexturePool>();
}
public TexturePool FindOrCreate(ulong address, int maximumId)
{
TexturePool pool;
// First we try to find the pool.
for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next)
{
pool = node.Value;
if (pool.Address == address)
{
if (pool.CacheNode != _pools.Last)
{
_pools.Remove(pool.CacheNode);
pool.CacheNode = _pools.AddLast(pool);
}
return pool;
}
}
// If not found, create a new one.
pool = new TexturePool(_context, _textureManager, address, maximumId);
pool.CacheNode = _pools.AddLast(pool);
if (_pools.Count > MaxCapacity)
{
TexturePool oldestPool = _pools.First.Value;
_pools.RemoveFirst();
oldestPool.Dispose();
oldestPool.CacheNode = null;
}
return pool;
}
public void InvalidateRange(ulong address, ulong size)
{
for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next)
{
TexturePool pool = node.Value;
pool.InvalidateRange(address, size);
}
}
}
}

View file

@ -0,0 +1,13 @@
using System;
namespace Ryujinx.Graphics.Gpu.Image
{
[Flags]
enum TextureSearchFlags
{
None = 0,
IgnoreMs = 1 << 0,
Strict = 1 << 1 | Sampler,
Sampler = 1 << 2
}
}

View file

@ -0,0 +1,49 @@
using Ryujinx.Graphics.GAL.Texture;
namespace Ryujinx.Graphics.Gpu.Image
{
enum TextureTarget
{
Texture1D,
Texture2D,
Texture3D,
Cubemap,
Texture1DArray,
Texture2DArray,
TextureBuffer,
Texture2DLinear,
CubemapArray
}
static class TextureTargetConverter
{
public static Target Convert(this TextureTarget target, bool isMultisample)
{
if (isMultisample)
{
switch (target)
{
case TextureTarget.Texture2D: return Target.Texture2DMultisample;
case TextureTarget.Texture2DArray: return Target.Texture2DMultisampleArray;
}
}
else
{
switch (target)
{
case TextureTarget.Texture1D: return Target.Texture1D;
case TextureTarget.Texture2D: return Target.Texture2D;
case TextureTarget.Texture2DLinear: return Target.Texture2D;
case TextureTarget.Texture3D: return Target.Texture3D;
case TextureTarget.Texture1DArray: return Target.Texture1DArray;
case TextureTarget.Texture2DArray: return Target.Texture2DArray;
case TextureTarget.Cubemap: return Target.Cubemap;
case TextureTarget.CubemapArray: return Target.CubemapArray;
case TextureTarget.TextureBuffer: return Target.TextureBuffer;
}
}
return Target.Texture1D;
}
}
}