Initial work
This commit is contained in:
parent
f617fb542a
commit
1876b346fe
518 changed files with 15170 additions and 12486 deletions
62
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
Normal file
62
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
Normal file
31
Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
201
Ryujinx.Graphics.Gpu/Image/FormatTable.cs
Normal file
201
Ryujinx.Graphics.Gpu/Image/FormatTable.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
99
Ryujinx.Graphics.Gpu/Image/Pool.cs
Normal file
99
Ryujinx.Graphics.Gpu/Image/Pool.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
Normal file
9
Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
enum ReductionFilter
|
||||
{
|
||||
Average,
|
||||
Minimum,
|
||||
Maximum
|
||||
}
|
||||
}
|
52
Ryujinx.Graphics.Gpu/Image/Sampler.cs
Normal file
52
Ryujinx.Graphics.Gpu/Image/Sampler.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
132
Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
Normal file
132
Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
61
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
Normal file
61
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
719
Ryujinx.Graphics.Gpu/Image/Texture.cs
Normal file
719
Ryujinx.Graphics.Gpu/Image/Texture.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
Normal file
17
Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
95
Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
Normal file
95
Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
35
Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
Normal file
35
Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
119
Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
Normal file
119
Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
Normal file
11
Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
enum TextureDescriptorType
|
||||
{
|
||||
Buffer,
|
||||
LinearColorKey,
|
||||
Linear,
|
||||
BlockLinear,
|
||||
BlockLinearColorKey
|
||||
}
|
||||
}
|
101
Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
Normal file
101
Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
669
Ryujinx.Graphics.Gpu/Image/TextureManager.cs
Normal file
669
Ryujinx.Graphics.Gpu/Image/TextureManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
53
Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
Normal file
53
Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
219
Ryujinx.Graphics.Gpu/Image/TexturePool.cs
Normal file
219
Ryujinx.Graphics.Gpu/Image/TexturePool.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
73
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
Normal file
73
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
Normal file
13
Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
Normal 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
|
||||
}
|
||||
}
|
49
Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
Normal file
49
Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue