aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Graphics.Gpu/Image
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image')
-rw-r--r--Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs256
-rw-r--r--Ryujinx.Graphics.Gpu/Image/FormatInfo.cs72
-rw-r--r--Ryujinx.Graphics.Gpu/Image/FormatTable.cs578
-rw-r--r--Ryujinx.Graphics.Gpu/Image/ITextureDescriptor.cs10
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Pool.cs222
-rw-r--r--Ryujinx.Graphics.Gpu/Image/PoolCache.cs129
-rw-r--r--Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs15
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Sampler.cs115
-rw-r--r--Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs260
-rw-r--r--Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs11
-rw-r--r--Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs12
-rw-r--r--Ryujinx.Graphics.Gpu/Image/SamplerPool.cs162
-rw-r--r--Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs30
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Texture.cs1705
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs73
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs882
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureCache.cs1180
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs911
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureComponent.cs43
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureDependency.cs37
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs273
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs16
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureGroup.cs1611
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs554
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureInfo.cs381
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureManager.cs498
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs9
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs68
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TexturePool.cs603
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs30
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs16
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs17
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureTarget.cs81
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs14
34 files changed, 0 insertions, 10874 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
deleted file mode 100644
index a0b9f57b..00000000
--- a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
+++ /dev/null
@@ -1,256 +0,0 @@
-using System.Collections;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// An entry on the short duration texture cache.
- /// </summary>
- class ShortTextureCacheEntry
- {
- public readonly TextureDescriptor Descriptor;
- public readonly int InvalidatedSequence;
- public readonly Texture Texture;
-
- /// <summary>
- /// Create a new entry on the short duration texture cache.
- /// </summary>
- /// <param name="descriptor">Last descriptor that referenced the texture</param>
- /// <param name="texture">The texture</param>
- public ShortTextureCacheEntry(TextureDescriptor descriptor, Texture texture)
- {
- Descriptor = descriptor;
- InvalidatedSequence = texture.InvalidatedSequence;
- Texture = texture;
- }
- }
-
- /// <summary>
- /// A texture cache that automatically removes older textures that are not used for some time.
- /// The cache works with a rotated list with a fixed size. When new textures are added, the
- /// old ones at the bottom of the list are deleted.
- /// </summary>
- class AutoDeleteCache : IEnumerable<Texture>
- {
- private const int MinCountForDeletion = 32;
- private const int MaxCapacity = 2048;
- private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB;
-
- private readonly LinkedList<Texture> _textures;
- private ulong _totalSize;
-
- private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
- private HashSet<ShortTextureCacheEntry> _shortCache;
-
- private Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
-
- /// <summary>
- /// Creates a new instance of the automatic deletion cache.
- /// </summary>
- public AutoDeleteCache()
- {
- _textures = new LinkedList<Texture>();
-
- _shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
- _shortCache = new HashSet<ShortTextureCacheEntry>();
-
- _shortCacheLookup = new Dictionary<TextureDescriptor, ShortTextureCacheEntry>();
- }
-
- /// <summary>
- /// Adds a new texture to the cache, even if the texture added is already on the cache.
- /// </summary>
- /// <remarks>
- /// Using this method is only recommended if you know that the texture is not yet on the cache,
- /// otherwise it would store the same texture more than once.
- /// </remarks>
- /// <param name="texture">The texture to be added to the cache</param>
- public void Add(Texture texture)
- {
- _totalSize += texture.Size;
-
- texture.IncrementReferenceCount();
- texture.CacheNode = _textures.AddLast(texture);
-
- if (_textures.Count > MaxCapacity ||
- (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
- {
- RemoveLeastUsedTexture();
- }
- }
-
- /// <summary>
- /// Adds a new texture to the cache, or just moves it to the top of the list if the
- /// texture is already on the cache.
- /// </summary>
- /// <remarks>
- /// Moving the texture to the top of the list prevents it from being deleted,
- /// as the textures on the bottom of the list are deleted when new ones are added.
- /// </remarks>
- /// <param name="texture">The texture to be added, or moved to the top</param>
- public void Lift(Texture texture)
- {
- if (texture.CacheNode != null)
- {
- if (texture.CacheNode != _textures.Last)
- {
- _textures.Remove(texture.CacheNode);
-
- texture.CacheNode = _textures.AddLast(texture);
- }
-
- if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
- {
- RemoveLeastUsedTexture();
- }
- }
- else
- {
- Add(texture);
- }
- }
-
- /// <summary>
- /// Removes the least used texture from the cache.
- /// </summary>
- private void RemoveLeastUsedTexture()
- {
- Texture oldestTexture = _textures.First.Value;
-
- _totalSize -= oldestTexture.Size;
-
- if (!oldestTexture.CheckModified(false))
- {
- // The texture must be flushed if it falls out of the auto delete cache.
- // Flushes out of the auto delete cache do not trigger write tracking,
- // as it is expected that other overlapping textures exist that have more up-to-date contents.
-
- oldestTexture.Group.SynchronizeDependents(oldestTexture);
- oldestTexture.FlushModified(false);
- }
-
- _textures.RemoveFirst();
-
- oldestTexture.DecrementReferenceCount();
- oldestTexture.CacheNode = null;
- }
-
- /// <summary>
- /// Removes a texture from the cache.
- /// </summary>
- /// <param name="texture">The texture to be removed from the cache</param>
- /// <param name="flush">True to remove the texture if it was on the cache</param>
- /// <returns>True if the texture was found and removed, false otherwise</returns>
- public bool Remove(Texture texture, bool flush)
- {
- if (texture.CacheNode == null)
- {
- return false;
- }
-
- // Remove our reference to this texture.
- if (flush)
- {
- texture.FlushModified(false);
- }
-
- _textures.Remove(texture.CacheNode);
-
- _totalSize -= texture.Size;
-
- texture.CacheNode = null;
-
- return texture.DecrementReferenceCount();
- }
-
- /// <summary>
- /// Attempt to find a texture on the short duration cache.
- /// </summary>
- /// <param name="descriptor">The texture descriptor</param>
- /// <returns>The texture if found, null otherwise</returns>
- public Texture FindShortCache(in TextureDescriptor descriptor)
- {
- if (_shortCacheLookup.Count > 0 && _shortCacheLookup.TryGetValue(descriptor, out var entry))
- {
- if (entry.InvalidatedSequence == entry.Texture.InvalidatedSequence)
- {
- return entry.Texture;
- }
- else
- {
- _shortCacheLookup.Remove(descriptor);
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Removes a texture from the short duration cache.
- /// </summary>
- /// <param name="texture">Texture to remove from the short cache</param>
- public void RemoveShortCache(Texture texture)
- {
- bool removed = _shortCache.Remove(texture.ShortCacheEntry);
- removed |= _shortCacheBuilder.Remove(texture.ShortCacheEntry);
-
- if (removed)
- {
- texture.DecrementReferenceCount();
-
- _shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor);
- texture.ShortCacheEntry = null;
- }
- }
-
- /// <summary>
- /// Adds a texture to the short duration cache.
- /// It starts in the builder set, and it is moved into the deletion set on next process.
- /// </summary>
- /// <param name="texture">Texture to add to the short cache</param>
- /// <param name="descriptor">Last used texture descriptor</param>
- public void AddShortCache(Texture texture, ref TextureDescriptor descriptor)
- {
- var entry = new ShortTextureCacheEntry(descriptor, texture);
-
- _shortCacheBuilder.Add(entry);
- _shortCacheLookup.Add(entry.Descriptor, entry);
-
- texture.ShortCacheEntry = entry;
-
- texture.IncrementReferenceCount();
- }
-
- /// <summary>
- /// Delete textures from the short duration cache.
- /// Moves the builder set to be deleted on next process.
- /// </summary>
- public void ProcessShortCache()
- {
- HashSet<ShortTextureCacheEntry> toRemove = _shortCache;
-
- foreach (var entry in toRemove)
- {
- entry.Texture.DecrementReferenceCount();
-
- _shortCacheLookup.Remove(entry.Descriptor);
- entry.Texture.ShortCacheEntry = null;
- }
-
- toRemove.Clear();
- _shortCache = _shortCacheBuilder;
- _shortCacheBuilder = toRemove;
- }
-
- public IEnumerator<Texture> GetEnumerator()
- {
- return _textures.GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return _textures.GetEnumerator();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs b/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
deleted file mode 100644
index 9ee649d2..00000000
--- a/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Represents texture format information.
- /// </summary>
- readonly struct FormatInfo
- {
- /// <summary>
- /// A default, generic RGBA8 texture format.
- /// </summary>
- public static FormatInfo Default { get; } = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
-
- /// <summary>
- /// The format of the texture data.
- /// </summary>
- public Format Format { get; }
-
- /// <summary>
- /// The block width for compressed formats.
- /// </summary>
- /// <remarks>
- /// Must be 1 for non-compressed formats.
- /// </remarks>
- public int BlockWidth { get; }
-
- /// <summary>
- /// The block height for compressed formats.
- /// </summary>
- /// <remarks>
- /// Must be 1 for non-compressed formats.
- /// </remarks>
- public int BlockHeight { get; }
-
- /// <summary>
- /// The number of bytes occupied by a single pixel in memory of the texture data.
- /// </summary>
- public int BytesPerPixel { get; }
-
- /// <summary>
- /// The maximum number of components this format has defined (in RGBA order).
- /// </summary>
- public int Components { get; }
-
- /// <summary>
- /// Whenever or not the texture format is a compressed format. Determined from block size.
- /// </summary>
- public bool IsCompressed => (BlockWidth | BlockHeight) != 1;
-
- /// <summary>
- /// Constructs the texture format info structure.
- /// </summary>
- /// <param name="format">The format of the texture data</param>
- /// <param name="blockWidth">The block width for compressed formats. Must be 1 for non-compressed formats</param>
- /// <param name="blockHeight">The block height for compressed formats. Must be 1 for non-compressed formats</param>
- /// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param>
- public FormatInfo(
- Format format,
- int blockWidth,
- int blockHeight,
- int bytesPerPixel,
- int components)
- {
- Format = format;
- BlockWidth = blockWidth;
- BlockHeight = blockHeight;
- BytesPerPixel = bytesPerPixel;
- Components = components;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/Ryujinx.Graphics.Gpu/Image/FormatTable.cs
deleted file mode 100644
index 72901610..00000000
--- a/Ryujinx.Graphics.Gpu/Image/FormatTable.cs
+++ /dev/null
@@ -1,578 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Contains format tables, for texture and vertex attribute formats.
- /// </summary>
- static class FormatTable
- {
- private enum TextureFormat : uint
- {
- // Formats
- R32G32B32A32 = 0x01,
- R32G32B32 = 0x02,
- R16G16B16A16 = 0x03,
- R32G32 = 0x04,
- R32B24G8 = 0x05,
- X8B8G8R8 = 0x07,
- A8B8G8R8 = 0x08,
- A2B10G10R10 = 0x09,
- R16G16 = 0x0c,
- G8R24 = 0x0d,
- G24R8 = 0x0e,
- R32 = 0x0f,
- A4B4G4R4 = 0x12,
- A5B5G5R1 = 0x13,
- A1B5G5R5 = 0x14,
- B5G6R5 = 0x15,
- B6G5R5 = 0x16,
- G8R8 = 0x18,
- R16 = 0x1b,
- Y8Video = 0x1c,
- R8 = 0x1d,
- G4R4 = 0x1e,
- R1 = 0x1f,
- E5B9G9R9SharedExp = 0x20,
- Bf10Gf11Rf11 = 0x21,
- G8B8G8R8 = 0x22,
- B8G8R8G8 = 0x23,
- Bc1 = 0x24,
- Bc2 = 0x25,
- Bc3 = 0x26,
- Bc4 = 0x27,
- Bc5 = 0x28,
- Bc6HSf16 = 0x10,
- Bc6HUf16 = 0x11,
- Bc7U = 0x17,
- Etc2Rgb = 0x06,
- Etc2RgbPta = 0x0a,
- Etc2Rgba = 0x0b,
- Eac = 0x19,
- Eacx2 = 0x1a,
- Z24S8 = 0x29,
- X8Z24 = 0x2a,
- S8Z24 = 0x2b,
- X4V4Z24Cov4R4V = 0x2c,
- X4V4Z24Cov8R8V = 0x2d,
- V8Z24Cov4R12V = 0x2e,
- Zf32 = 0x2f,
- Zf32X24S8 = 0x30,
- X8Z24X20V4S8Cov4R4V = 0x31,
- X8Z24X20V4S8Cov8R8V = 0x32,
- Zf32X20V4X8Cov4R4V = 0x33,
- Zf32X20V4X8Cov8R8V = 0x34,
- Zf32X20V4S8Cov4R4V = 0x35,
- Zf32X20V4S8Cov8R8V = 0x36,
- X8Z24X16V8S8Cov4R12V = 0x37,
- Zf32X16V8X8Cov4R12V = 0x38,
- Zf32X16V8S8Cov4R12V = 0x39,
- Z16 = 0x3a,
- V8Z24Cov8R24V = 0x3b,
- X8Z24X16V8S8Cov8R24V = 0x3c,
- Zf32X16V8X8Cov8R24V = 0x3d,
- Zf32X16V8S8Cov8R24V = 0x3e,
- Astc2D4x4 = 0x40,
- Astc2D5x4 = 0x50,
- Astc2D5x5 = 0x41,
- Astc2D6x5 = 0x51,
- Astc2D6x6 = 0x42,
- Astc2D8x5 = 0x55,
- Astc2D8x6 = 0x52,
- Astc2D8x8 = 0x44,
- Astc2D10x5 = 0x56,
- Astc2D10x6 = 0x57,
- Astc2D10x8 = 0x53,
- Astc2D10x10 = 0x45,
- Astc2D12x10 = 0x54,
- Astc2D12x12 = 0x46,
-
- // Types
- Snorm = 0x1,
- Unorm = 0x2,
- Sint = 0x3,
- Uint = 0x4,
- SnormForceFp16 = 0x5,
- UnormForceFp16 = 0x6,
- Float = 0x7,
-
- // Component Types
- RSnorm = Snorm << 7,
- GSnorm = Snorm << 10,
- BSnorm = Snorm << 13,
- ASnorm = Snorm << 16,
-
- RUnorm = Unorm << 7,
- GUnorm = Unorm << 10,
- BUnorm = Unorm << 13,
- AUnorm = Unorm << 16,
-
- RSint = Sint << 7,
- GSint = Sint << 10,
- BSint = Sint << 13,
- ASint = Sint << 16,
-
- RUint = Uint << 7,
- GUint = Uint << 10,
- BUint = Uint << 13,
- AUint = Uint << 16,
-
- RSnormForceFp16 = SnormForceFp16 << 7,
- GSnormForceFp16 = SnormForceFp16 << 10,
- BSnormForceFp16 = SnormForceFp16 << 13,
- ASnormForceFp16 = SnormForceFp16 << 16,
-
- RUnormForceFp16 = UnormForceFp16 << 7,
- GUnormForceFp16 = UnormForceFp16 << 10,
- BUnormForceFp16 = UnormForceFp16 << 13,
- AUnormForceFp16 = UnormForceFp16 << 16,
-
- RFloat = Float << 7,
- GFloat = Float << 10,
- BFloat = Float << 13,
- AFloat = Float << 16,
-
- Srgb = 0x1 << 19, // Custom encoding
-
- // Combinations
- R8Unorm = R8 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2491d
- R8Snorm = R8 | RSnorm | GSnorm | BSnorm | ASnorm, // 0x1249d
- R8Uint = R8 | RUint | GUint | BUint | AUint, // 0x4921d
- R8Sint = R8 | RSint | GSint | BSint | ASint, // 0x36d9d
- R16Float = R16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff9b
- R16Unorm = R16 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2491b
- R16Snorm = R16 | RSnorm | GSnorm | BSnorm | ASnorm, // 0x1249b
- R16Uint = R16 | RUint | GUint | BUint | AUint, // 0x4921b
- R16Sint = R16 | RSint | GSint | BSint | ASint, // 0x36d9b
- R32Float = R32 | RFloat | GFloat | BFloat | AFloat, // 0x7ff8f
- R32Uint = R32 | RUint | GUint | BUint | AUint, // 0x4920f
- R32Sint = R32 | RSint | GSint | BSint | ASint, // 0x36d8f
- G8R8Unorm = G8R8 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24918
- G8R8Snorm = G8R8 | RSnorm | GSnorm | BSnorm | ASnorm, // 0x12498
- G8R8Uint = G8R8 | RUint | GUint | BUint | AUint, // 0x49218
- G8R8Sint = G8R8 | RSint | GSint | BSint | ASint, // 0x36d98
- R16G16Float = R16G16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff8c
- R16G16Unorm = R16G16 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490c
- R16G16Snorm = R16G16 | RSnorm | GSnorm | BSnorm | ASnorm, // 0x1248c
- R16G16Uint = R16G16 | RUint | GUint | BUint | AUint, // 0x4920c
- R16G16Sint = R16G16 | RSint | GSint | BSint | ASint, // 0x36d8c
- R32G32Float = R32G32 | RFloat | GFloat | BFloat | AFloat, // 0x7ff84
- R32G32Uint = R32G32 | RUint | GUint | BUint | AUint, // 0x49204
- R32G32Sint = R32G32 | RSint | GSint | BSint | ASint, // 0x36d84
- R32G32B32Float = R32G32B32 | RFloat | GFloat | BFloat | AFloat, // 0x7ff82
- R32G32B32Uint = R32G32B32 | RUint | GUint | BUint | AUint, // 0x49202
- R32G32B32Sint = R32G32B32 | RSint | GSint | BSint | ASint, // 0x36d82
- A8B8G8R8Unorm = A8B8G8R8 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24908
- A8B8G8R8Snorm = A8B8G8R8 | RSnorm | GSnorm | BSnorm | ASnorm, // 0x12488
- A8B8G8R8Uint = A8B8G8R8 | RUint | GUint | BUint | AUint, // 0x49208
- A8B8G8R8Sint = A8B8G8R8 | RSint | GSint | BSint | ASint, // 0x36d88
- R16G16B16A16Float = R16G16B16A16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff83
- R16G16B16A16Unorm = R16G16B16A16 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24903
- R16G16B16A16Snorm = R16G16B16A16 | RSnorm | GSnorm | BSnorm | ASnorm, // 0x12483
- R16G16B16A16Uint = R16G16B16A16 | RUint | GUint | BUint | AUint, // 0x49203
- R16G16B16A16Sint = R16G16B16A16 | RSint | GSint | BSint | ASint, // 0x36d83
- R32G32B32A32Float = R32G32B32A32 | RFloat | GFloat | BFloat | AFloat, // 0x7ff81
- R32G32B32A32Uint = R32G32B32A32 | RUint | GUint | BUint | AUint, // 0x49201
- R32G32B32A32Sint = R32G32B32A32 | RSint | GSint | BSint | ASint, // 0x36d81
- Z16Unorm = Z16 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2493a
- Zf32RFloatGUintBUintAUint = Zf32 | RFloat | GUint | BUint | AUint, // 0x493af
- Zf32Float = Zf32 | RFloat | GFloat | BFloat | AFloat, // 0x7ffaf
- G24R8RUintGUnormBUnormAUnorm = G24R8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a0e
- Z24S8RUintGUnormBUnormAUnorm = Z24S8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a29
- Z24S8RUintGUnormBUintAUint = Z24S8 | RUint | GUnorm | BUint | AUint, // 0x48a29
- S8Z24RUnormGUintBUintAUint = S8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912b
- R32B24G8RFloatGUintBUnormAUnorm = R32B24G8 | RFloat | GUint | BUnorm | AUnorm, // 0x25385
- Zf32X24S8RFloatGUintBUnormAUnorm = Zf32X24S8 | RFloat | GUint | BUnorm | AUnorm, // 0x253b0
- A8B8G8R8UnormSrgb = A8B8G8R8 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4908
- G4R4Unorm = G4R4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2491e
- A4B4G4R4Unorm = A4B4G4R4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24912
- A1B5G5R5Unorm = A1B5G5R5 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24914
- B5G6R5Unorm = B5G6R5 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24915
- A2B10G10R10Unorm = A2B10G10R10 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24909
- A2B10G10R10Uint = A2B10G10R10 | RUint | GUint | BUint | AUint, // 0x49209
- Bf10Gf11Rf11Float = Bf10Gf11Rf11 | RFloat | GFloat | BFloat | AFloat, // 0x7ffa1
- E5B9G9R9SharedExpFloat = E5B9G9R9SharedExp | RFloat | GFloat | BFloat | AFloat, // 0x7ffa0
- Bc1Unorm = Bc1 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24924
- Bc2Unorm = Bc2 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24925
- Bc3Unorm = Bc3 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24926
- Bc1UnormSrgb = Bc1 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4924
- Bc2UnormSrgb = Bc2 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4925
- Bc3UnormSrgb = Bc3 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4926
- Bc4Unorm = Bc4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24927
- Bc4Snorm = Bc4 | RSnorm | GSnorm | BSnorm | ASnorm, // 0x124a7
- Bc5Unorm = Bc5 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24928
- Bc5Snorm = Bc5 | RSnorm | GSnorm | BSnorm | ASnorm, // 0x124a8
- Bc7UUnorm = Bc7U | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24917
- Bc7UUnormSrgb = Bc7U | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4917
- Bc6HSf16Float = Bc6HSf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff90
- Bc6HUf16Float = Bc6HUf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff91
- Etc2RgbUnorm = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24906
- Etc2RgbPtaUnorm = Etc2RgbPta | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490a
- Etc2RgbaUnorm = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490b
- Etc2RgbUnormSrgb = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4906
- Etc2RgbPtaUnormSrgb = Etc2RgbPta | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490a
- Etc2RgbaUnormSrgb = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490b
- Astc2D4x4Unorm = Astc2D4x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24940
- Astc2D5x4Unorm = Astc2D5x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24950
- Astc2D5x5Unorm = Astc2D5x5 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24941
- Astc2D6x5Unorm = Astc2D6x5 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24951
- Astc2D6x6Unorm = Astc2D6x6 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24942
- Astc2D8x5Unorm = Astc2D8x5 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24955
- Astc2D8x6Unorm = Astc2D8x6 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24952
- Astc2D8x8Unorm = Astc2D8x8 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24944
- Astc2D10x5Unorm = Astc2D10x5 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24956
- Astc2D10x6Unorm = Astc2D10x6 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24957
- Astc2D10x8Unorm = Astc2D10x8 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24953
- Astc2D10x10Unorm = Astc2D10x10 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24945
- Astc2D12x10Unorm = Astc2D12x10 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24954
- Astc2D12x12Unorm = Astc2D12x12 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24946
- Astc2D4x4UnormSrgb = Astc2D4x4 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4940
- Astc2D5x4UnormSrgb = Astc2D5x4 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4950
- Astc2D5x5UnormSrgb = Astc2D5x5 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4941
- Astc2D6x5UnormSrgb = Astc2D6x5 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4951
- Astc2D6x6UnormSrgb = Astc2D6x6 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4942
- Astc2D8x5UnormSrgb = Astc2D8x5 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4955
- Astc2D8x6UnormSrgb = Astc2D8x6 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4952
- Astc2D8x8UnormSrgb = Astc2D8x8 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4944
- Astc2D10x5UnormSrgb = Astc2D10x5 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4956
- Astc2D10x6UnormSrgb = Astc2D10x6 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4957
- Astc2D10x8UnormSrgb = Astc2D10x8 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4953
- Astc2D10x10UnormSrgb = Astc2D10x10 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4945
- Astc2D12x10UnormSrgb = Astc2D12x10 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4954
- Astc2D12x12UnormSrgb = Astc2D12x12 | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4946
- A5B5G5R1Unorm = A5B5G5R1 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24913
- }
-
- private enum VertexAttributeFormat : uint
- {
- // Width
- R32G32B32A32 = 0x01,
- R32G32B32 = 0x02,
- R16G16B16A16 = 0x03,
- R32G32 = 0x04,
- R16G16B16 = 0x05,
- A8B8G8R8 = 0x2f,
- R8G8B8A8 = 0x0a,
- X8B8G8R8 = 0x33,
- A2B10G10R10 = 0x30,
- B10G11R11 = 0x31,
- R16G16 = 0x0f,
- R32 = 0x12,
- R8G8B8 = 0x13,
- G8R8 = 0x32,
- R8G8 = 0x18,
- R16 = 0x1b,
- R8 = 0x1d,
- A8 = 0x34,
-
- // Type
- Snorm = 0x01,
- Unorm = 0x02,
- Sint = 0x03,
- Uint = 0x04,
- Uscaled = 0x05,
- Sscaled = 0x06,
- Float = 0x07,
-
- // Combinations
- R8Unorm = (R8 << 21) | (Unorm << 27), // 0x13a00000
- R8Snorm = (R8 << 21) | (Snorm << 27), // 0x0ba00000
- R8Uint = (R8 << 21) | (Uint << 27), // 0x23a00000
- R8Sint = (R8 << 21) | (Sint << 27), // 0x1ba00000
- R16Float = (R16 << 21) | (Float << 27), // 0x3b600000
- R16Unorm = (R16 << 21) | (Unorm << 27), // 0x13600000
- R16Snorm = (R16 << 21) | (Snorm << 27), // 0x0b600000
- R16Uint = (R16 << 21) | (Uint << 27), // 0x23600000
- R16Sint = (R16 << 21) | (Sint << 27), // 0x1b600000
- R32Float = (R32 << 21) | (Float << 27), // 0x3a400000
- R32Uint = (R32 << 21) | (Uint << 27), // 0x22400000
- R32Sint = (R32 << 21) | (Sint << 27), // 0x1a400000
- R8G8Unorm = (R8G8 << 21) | (Unorm << 27), // 0x13000000
- R8G8Snorm = (R8G8 << 21) | (Snorm << 27), // 0x0b000000
- R8G8Uint = (R8G8 << 21) | (Uint << 27), // 0x23000000
- R8G8Sint = (R8G8 << 21) | (Sint << 27), // 0x1b000000
- R16G16Float = (R16G16 << 21) | (Float << 27), // 0x39e00000
- R16G16Unorm = (R16G16 << 21) | (Unorm << 27), // 0x11e00000
- R16G16Snorm = (R16G16 << 21) | (Snorm << 27), // 0x09e00000
- R16G16Uint = (R16G16 << 21) | (Uint << 27), // 0x21e00000
- R16G16Sint = (R16G16 << 21) | (Sint << 27), // 0x19e00000
- R32G32Float = (R32G32 << 21) | (Float << 27), // 0x38800000
- R32G32Uint = (R32G32 << 21) | (Uint << 27), // 0x20800000
- R32G32Sint = (R32G32 << 21) | (Sint << 27), // 0x18800000
- R8G8B8Unorm = (R8G8B8 << 21) | (Unorm << 27), // 0x12600000
- R8G8B8Snorm = (R8G8B8 << 21) | (Snorm << 27), // 0x0a600000
- R8G8B8Uint = (R8G8B8 << 21) | (Uint << 27), // 0x22600000
- R8G8B8Sint = (R8G8B8 << 21) | (Sint << 27), // 0x1a600000
- R16G16B16Float = (R16G16B16 << 21) | (Float << 27), // 0x38a00000
- R16G16B16Unorm = (R16G16B16 << 21) | (Unorm << 27), // 0x10a00000
- R16G16B16Snorm = (R16G16B16 << 21) | (Snorm << 27), // 0x08a00000
- R16G16B16Uint = (R16G16B16 << 21) | (Uint << 27), // 0x20a00000
- R16G16B16Sint = (R16G16B16 << 21) | (Sint << 27), // 0x18a00000
- R32G32B32Float = (R32G32B32 << 21) | (Float << 27), // 0x38400000
- R32G32B32Uint = (R32G32B32 << 21) | (Uint << 27), // 0x20400000
- R32G32B32Sint = (R32G32B32 << 21) | (Sint << 27), // 0x18400000
- R8G8B8A8Unorm = (R8G8B8A8 << 21) | (Unorm << 27), // 0x11400000
- R8G8B8A8Snorm = (R8G8B8A8 << 21) | (Snorm << 27), // 0x09400000
- R8G8B8A8Uint = (R8G8B8A8 << 21) | (Uint << 27), // 0x21400000
- R8G8B8A8Sint = (R8G8B8A8 << 21) | (Sint << 27), // 0x19400000
- R16G16B16A16Float = (R16G16B16A16 << 21) | (Float << 27), // 0x38600000
- R16G16B16A16Unorm = (R16G16B16A16 << 21) | (Unorm << 27), // 0x10600000
- R16G16B16A16Snorm = (R16G16B16A16 << 21) | (Snorm << 27), // 0x08600000
- R16G16B16A16Uint = (R16G16B16A16 << 21) | (Uint << 27), // 0x20600000
- R16G16B16A16Sint = (R16G16B16A16 << 21) | (Sint << 27), // 0x18600000
- R32G32B32A32Float = (R32G32B32A32 << 21) | (Float << 27), // 0x38200000
- R32G32B32A32Uint = (R32G32B32A32 << 21) | (Uint << 27), // 0x20200000
- R32G32B32A32Sint = (R32G32B32A32 << 21) | (Sint << 27), // 0x18200000
- A2B10G10R10Unorm = (A2B10G10R10 << 21) | (Unorm << 27), // 0x16000000
- A2B10G10R10Uint = (A2B10G10R10 << 21) | (Uint << 27), // 0x26000000
- B10G11R11Float = (B10G11R11 << 21) | (Float << 27), // 0x3e200000
- R8Uscaled = (R8 << 21) | (Uscaled << 27), // 0x2ba00000
- R8Sscaled = (R8 << 21) | (Sscaled << 27), // 0x33a00000
- R16Uscaled = (R16 << 21) | (Uscaled << 27), // 0x2b600000
- R16Sscaled = (R16 << 21) | (Sscaled << 27), // 0x33600000
- R32Uscaled = (R32 << 21) | (Uscaled << 27), // 0x2a400000
- R32Sscaled = (R32 << 21) | (Sscaled << 27), // 0x32400000
- R8G8Uscaled = (R8G8 << 21) | (Uscaled << 27), // 0x2b000000
- R8G8Sscaled = (R8G8 << 21) | (Sscaled << 27), // 0x33000000
- R16G16Uscaled = (R16G16 << 21) | (Uscaled << 27), // 0x29e00000
- R16G16Sscaled = (R16G16 << 21) | (Sscaled << 27), // 0x31e00000
- R32G32Uscaled = (R32G32 << 21) | (Uscaled << 27), // 0x28800000
- R32G32Sscaled = (R32G32 << 21) | (Sscaled << 27), // 0x30800000
- R8G8B8Uscaled = (R8G8B8 << 21) | (Uscaled << 27), // 0x2a600000
- R8G8B8Sscaled = (R8G8B8 << 21) | (Sscaled << 27), // 0x32600000
- R16G16B16Uscaled = (R16G16B16 << 21) | (Uscaled << 27), // 0x28a00000
- R16G16B16Sscaled = (R16G16B16 << 21) | (Sscaled << 27), // 0x30a00000
- R32G32B32Uscaled = (R32G32B32 << 21) | (Uscaled << 27), // 0x28400000
- R32G32B32Sscaled = (R32G32B32 << 21) | (Sscaled << 27), // 0x30400000
- R8G8B8A8Uscaled = (R8G8B8A8 << 21) | (Uscaled << 27), // 0x29400000
- R8G8B8A8Sscaled = (R8G8B8A8 << 21) | (Sscaled << 27), // 0x31400000
- R16G16B16A16Uscaled = (R16G16B16A16 << 21) | (Uscaled << 27), // 0x28600000
- R16G16B16A16Sscaled = (R16G16B16A16 << 21) | (Sscaled << 27), // 0x30600000
- R32G32B32A32Uscaled = (R32G32B32A32 << 21) | (Uscaled << 27), // 0x28200000
- R32G32B32A32Sscaled = (R32G32B32A32 << 21) | (Sscaled << 27), // 0x30200000
- A2B10G10R10Snorm = (A2B10G10R10 << 21) | (Snorm << 27), // 0x0e000000
- A2B10G10R10Sint = (A2B10G10R10 << 21) | (Sint << 27), // 0x1e000000
- A2B10G10R10Uscaled = (A2B10G10R10 << 21) | (Uscaled << 27), // 0x2e000000
- A2B10G10R10Sscaled = (A2B10G10R10 << 21) | (Sscaled << 27), // 0x36000000
- }
-
- private static readonly Dictionary<TextureFormat, FormatInfo> _textureFormats = new Dictionary<TextureFormat, FormatInfo>()
- {
- { TextureFormat.R8Unorm, new FormatInfo(Format.R8Unorm, 1, 1, 1, 1) },
- { TextureFormat.R8Snorm, new FormatInfo(Format.R8Snorm, 1, 1, 1, 1) },
- { TextureFormat.R8Uint, new FormatInfo(Format.R8Uint, 1, 1, 1, 1) },
- { TextureFormat.R8Sint, new FormatInfo(Format.R8Sint, 1, 1, 1, 1) },
- { TextureFormat.R16Float, new FormatInfo(Format.R16Float, 1, 1, 2, 1) },
- { TextureFormat.R16Unorm, new FormatInfo(Format.R16Unorm, 1, 1, 2, 1) },
- { TextureFormat.R16Snorm, new FormatInfo(Format.R16Snorm, 1, 1, 2, 1) },
- { TextureFormat.R16Uint, new FormatInfo(Format.R16Uint, 1, 1, 2, 1) },
- { TextureFormat.R16Sint, new FormatInfo(Format.R16Sint, 1, 1, 2, 1) },
- { TextureFormat.R32Float, new FormatInfo(Format.R32Float, 1, 1, 4, 1) },
- { TextureFormat.R32Uint, new FormatInfo(Format.R32Uint, 1, 1, 4, 1) },
- { TextureFormat.R32Sint, new FormatInfo(Format.R32Sint, 1, 1, 4, 1) },
- { TextureFormat.G8R8Unorm, new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2) },
- { TextureFormat.G8R8Snorm, new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2) },
- { TextureFormat.G8R8Uint, new FormatInfo(Format.R8G8Uint, 1, 1, 2, 2) },
- { TextureFormat.G8R8Sint, new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2) },
- { TextureFormat.R16G16Float, new FormatInfo(Format.R16G16Float, 1, 1, 4, 2) },
- { TextureFormat.R16G16Unorm, new FormatInfo(Format.R16G16Unorm, 1, 1, 4, 2) },
- { TextureFormat.R16G16Snorm, new FormatInfo(Format.R16G16Snorm, 1, 1, 4, 2) },
- { TextureFormat.R16G16Uint, new FormatInfo(Format.R16G16Uint, 1, 1, 4, 2) },
- { TextureFormat.R16G16Sint, new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2) },
- { TextureFormat.R32G32Float, new FormatInfo(Format.R32G32Float, 1, 1, 8, 2) },
- { TextureFormat.R32G32Uint, new FormatInfo(Format.R32G32Uint, 1, 1, 8, 2) },
- { TextureFormat.R32G32Sint, new FormatInfo(Format.R32G32Sint, 1, 1, 8, 2) },
- { TextureFormat.R32G32B32Float, new FormatInfo(Format.R32G32B32Float, 1, 1, 12, 3) },
- { TextureFormat.R32G32B32Uint, new FormatInfo(Format.R32G32B32Uint, 1, 1, 12, 3) },
- { TextureFormat.R32G32B32Sint, new FormatInfo(Format.R32G32B32Sint, 1, 1, 12, 3) },
- { TextureFormat.A8B8G8R8Unorm, new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4) },
- { TextureFormat.A8B8G8R8Snorm, new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4, 4) },
- { TextureFormat.A8B8G8R8Uint, new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4, 4) },
- { TextureFormat.A8B8G8R8Sint, new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4) },
- { TextureFormat.R16G16B16A16Float, new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4) },
- { TextureFormat.R16G16B16A16Unorm, new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8, 4) },
- { TextureFormat.R16G16B16A16Snorm, new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8, 4) },
- { TextureFormat.R16G16B16A16Uint, new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8, 4) },
- { TextureFormat.R16G16B16A16Sint, new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4) },
- { TextureFormat.R32G32B32A32Float, new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4) },
- { TextureFormat.R32G32B32A32Uint, new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4) },
- { TextureFormat.R32G32B32A32Sint, new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4) },
- { TextureFormat.Z16Unorm, new FormatInfo(Format.D16Unorm, 1, 1, 2, 1) },
- { TextureFormat.Zf32RFloatGUintBUintAUint, new FormatInfo(Format.D32Float, 1, 1, 4, 1) },
- { TextureFormat.Zf32Float, new FormatInfo(Format.D32Float, 1, 1, 4, 1) },
- { TextureFormat.G24R8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
- { TextureFormat.Z24S8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
- { TextureFormat.Z24S8RUintGUnormBUintAUint, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
- { TextureFormat.S8Z24RUnormGUintBUintAUint, new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2) },
- { TextureFormat.R32B24G8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
- { TextureFormat.Zf32X24S8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
- { TextureFormat.A8B8G8R8UnormSrgb, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4) },
- { TextureFormat.G4R4Unorm, new FormatInfo(Format.R4G4Unorm, 1, 1, 1, 2) },
- { TextureFormat.A4B4G4R4Unorm, new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4) },
- { TextureFormat.A1B5G5R5Unorm, new FormatInfo(Format.R5G5B5A1Unorm, 1, 1, 2, 4) },
- { TextureFormat.B5G6R5Unorm, new FormatInfo(Format.R5G6B5Unorm, 1, 1, 2, 3) },
- { TextureFormat.A2B10G10R10Unorm, new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4, 4) },
- { TextureFormat.A2B10G10R10Uint, new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4, 4) },
- { TextureFormat.Bf10Gf11Rf11Float, new FormatInfo(Format.R11G11B10Float, 1, 1, 4, 3) },
- { TextureFormat.E5B9G9R9SharedExpFloat, new FormatInfo(Format.R9G9B9E5Float, 1, 1, 4, 4) },
- { TextureFormat.Bc1Unorm, new FormatInfo(Format.Bc1RgbaUnorm, 4, 4, 8, 4) },
- { TextureFormat.Bc2Unorm, new FormatInfo(Format.Bc2Unorm, 4, 4, 16, 4) },
- { TextureFormat.Bc3Unorm, new FormatInfo(Format.Bc3Unorm, 4, 4, 16, 4) },
- { TextureFormat.Bc1UnormSrgb, new FormatInfo(Format.Bc1RgbaSrgb, 4, 4, 8, 4) },
- { TextureFormat.Bc2UnormSrgb, new FormatInfo(Format.Bc2Srgb, 4, 4, 16, 4) },
- { TextureFormat.Bc3UnormSrgb, new FormatInfo(Format.Bc3Srgb, 4, 4, 16, 4) },
- { TextureFormat.Bc4Unorm, new FormatInfo(Format.Bc4Unorm, 4, 4, 8, 1) },
- { TextureFormat.Bc4Snorm, new FormatInfo(Format.Bc4Snorm, 4, 4, 8, 1) },
- { TextureFormat.Bc5Unorm, new FormatInfo(Format.Bc5Unorm, 4, 4, 16, 2) },
- { TextureFormat.Bc5Snorm, new FormatInfo(Format.Bc5Snorm, 4, 4, 16, 2) },
- { TextureFormat.Bc7UUnorm, new FormatInfo(Format.Bc7Unorm, 4, 4, 16, 4) },
- { TextureFormat.Bc7UUnormSrgb, new FormatInfo(Format.Bc7Srgb, 4, 4, 16, 4) },
- { TextureFormat.Bc6HSf16Float, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16, 4) },
- { TextureFormat.Bc6HUf16Float, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16, 4) },
- { TextureFormat.Etc2RgbUnorm, new FormatInfo(Format.Etc2RgbUnorm, 4, 4, 8, 3) },
- { TextureFormat.Etc2RgbPtaUnorm, new FormatInfo(Format.Etc2RgbPtaUnorm, 4, 4, 8, 4) },
- { TextureFormat.Etc2RgbaUnorm, new FormatInfo(Format.Etc2RgbaUnorm, 4, 4, 16, 4) },
- { TextureFormat.Etc2RgbUnormSrgb, new FormatInfo(Format.Etc2RgbSrgb, 4, 4, 8, 3) },
- { TextureFormat.Etc2RgbPtaUnormSrgb, new FormatInfo(Format.Etc2RgbPtaSrgb, 4, 4, 8, 4) },
- { TextureFormat.Etc2RgbaUnormSrgb, new FormatInfo(Format.Etc2RgbaSrgb, 4, 4, 16, 4) },
- { TextureFormat.Astc2D4x4Unorm, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16, 4) },
- { TextureFormat.Astc2D5x4Unorm, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16, 4) },
- { TextureFormat.Astc2D5x5Unorm, new FormatInfo(Format.Astc5x5Unorm, 5, 5, 16, 4) },
- { TextureFormat.Astc2D6x5Unorm, new FormatInfo(Format.Astc6x5Unorm, 6, 5, 16, 4) },
- { TextureFormat.Astc2D6x6Unorm, new FormatInfo(Format.Astc6x6Unorm, 6, 6, 16, 4) },
- { TextureFormat.Astc2D8x5Unorm, new FormatInfo(Format.Astc8x5Unorm, 8, 5, 16, 4) },
- { TextureFormat.Astc2D8x6Unorm, new FormatInfo(Format.Astc8x6Unorm, 8, 6, 16, 4) },
- { TextureFormat.Astc2D8x8Unorm, new FormatInfo(Format.Astc8x8Unorm, 8, 8, 16, 4) },
- { TextureFormat.Astc2D10x5Unorm, new FormatInfo(Format.Astc10x5Unorm, 10, 5, 16, 4) },
- { TextureFormat.Astc2D10x6Unorm, new FormatInfo(Format.Astc10x6Unorm, 10, 6, 16, 4) },
- { TextureFormat.Astc2D10x8Unorm, new FormatInfo(Format.Astc10x8Unorm, 10, 8, 16, 4) },
- { TextureFormat.Astc2D10x10Unorm, new FormatInfo(Format.Astc10x10Unorm, 10, 10, 16, 4) },
- { TextureFormat.Astc2D12x10Unorm, new FormatInfo(Format.Astc12x10Unorm, 12, 10, 16, 4) },
- { TextureFormat.Astc2D12x12Unorm, new FormatInfo(Format.Astc12x12Unorm, 12, 12, 16, 4) },
- { TextureFormat.Astc2D4x4UnormSrgb, new FormatInfo(Format.Astc4x4Srgb, 4, 4, 16, 4) },
- { TextureFormat.Astc2D5x4UnormSrgb, new FormatInfo(Format.Astc5x4Srgb, 5, 4, 16, 4) },
- { TextureFormat.Astc2D5x5UnormSrgb, new FormatInfo(Format.Astc5x5Srgb, 5, 5, 16, 4) },
- { TextureFormat.Astc2D6x5UnormSrgb, new FormatInfo(Format.Astc6x5Srgb, 6, 5, 16, 4) },
- { TextureFormat.Astc2D6x6UnormSrgb, new FormatInfo(Format.Astc6x6Srgb, 6, 6, 16, 4) },
- { TextureFormat.Astc2D8x5UnormSrgb, new FormatInfo(Format.Astc8x5Srgb, 8, 5, 16, 4) },
- { TextureFormat.Astc2D8x6UnormSrgb, new FormatInfo(Format.Astc8x6Srgb, 8, 6, 16, 4) },
- { TextureFormat.Astc2D8x8UnormSrgb, new FormatInfo(Format.Astc8x8Srgb, 8, 8, 16, 4) },
- { TextureFormat.Astc2D10x5UnormSrgb, new FormatInfo(Format.Astc10x5Srgb, 10, 5, 16, 4) },
- { TextureFormat.Astc2D10x6UnormSrgb, new FormatInfo(Format.Astc10x6Srgb, 10, 6, 16, 4) },
- { TextureFormat.Astc2D10x8UnormSrgb, new FormatInfo(Format.Astc10x8Srgb, 10, 8, 16, 4) },
- { TextureFormat.Astc2D10x10UnormSrgb, new FormatInfo(Format.Astc10x10Srgb, 10, 10, 16, 4) },
- { TextureFormat.Astc2D12x10UnormSrgb, new FormatInfo(Format.Astc12x10Srgb, 12, 10, 16, 4) },
- { TextureFormat.Astc2D12x12UnormSrgb, new FormatInfo(Format.Astc12x12Srgb, 12, 12, 16, 4) },
- { TextureFormat.A5B5G5R1Unorm, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2, 4) }
- };
-
- private static readonly Dictionary<VertexAttributeFormat, Format> _attribFormats = new Dictionary<VertexAttributeFormat, Format>()
- {
- { VertexAttributeFormat.R8Unorm, Format.R8Unorm },
- { VertexAttributeFormat.R8Snorm, Format.R8Snorm },
- { VertexAttributeFormat.R8Uint, Format.R8Uint },
- { VertexAttributeFormat.R8Sint, Format.R8Sint },
- { VertexAttributeFormat.R16Float, Format.R16Float },
- { VertexAttributeFormat.R16Unorm, Format.R16Unorm },
- { VertexAttributeFormat.R16Snorm, Format.R16Snorm },
- { VertexAttributeFormat.R16Uint, Format.R16Uint },
- { VertexAttributeFormat.R16Sint, Format.R16Sint },
- { VertexAttributeFormat.R32Float, Format.R32Float },
- { VertexAttributeFormat.R32Uint, Format.R32Uint },
- { VertexAttributeFormat.R32Sint, Format.R32Sint },
- { VertexAttributeFormat.R8G8Unorm, Format.R8G8Unorm },
- { VertexAttributeFormat.R8G8Snorm, Format.R8G8Snorm },
- { VertexAttributeFormat.R8G8Uint, Format.R8G8Uint },
- { VertexAttributeFormat.R8G8Sint, Format.R8G8Sint },
- { VertexAttributeFormat.R16G16Float, Format.R16G16Float },
- { VertexAttributeFormat.R16G16Unorm, Format.R16G16Unorm },
- { VertexAttributeFormat.R16G16Snorm, Format.R16G16Snorm },
- { VertexAttributeFormat.R16G16Uint, Format.R16G16Uint },
- { VertexAttributeFormat.R16G16Sint, Format.R16G16Sint },
- { VertexAttributeFormat.R32G32Float, Format.R32G32Float },
- { VertexAttributeFormat.R32G32Uint, Format.R32G32Uint },
- { VertexAttributeFormat.R32G32Sint, Format.R32G32Sint },
- { VertexAttributeFormat.R8G8B8Unorm, Format.R8G8B8Unorm },
- { VertexAttributeFormat.R8G8B8Snorm, Format.R8G8B8Snorm },
- { VertexAttributeFormat.R8G8B8Uint, Format.R8G8B8Uint },
- { VertexAttributeFormat.R8G8B8Sint, Format.R8G8B8Sint },
- { VertexAttributeFormat.R16G16B16Float, Format.R16G16B16Float },
- { VertexAttributeFormat.R16G16B16Unorm, Format.R16G16B16Unorm },
- { VertexAttributeFormat.R16G16B16Snorm, Format.R16G16B16Snorm },
- { VertexAttributeFormat.R16G16B16Uint, Format.R16G16B16Uint },
- { VertexAttributeFormat.R16G16B16Sint, Format.R16G16B16Sint },
- { VertexAttributeFormat.R32G32B32Float, Format.R32G32B32Float },
- { VertexAttributeFormat.R32G32B32Uint, Format.R32G32B32Uint },
- { VertexAttributeFormat.R32G32B32Sint, Format.R32G32B32Sint },
- { VertexAttributeFormat.R8G8B8A8Unorm, Format.R8G8B8A8Unorm },
- { VertexAttributeFormat.R8G8B8A8Snorm, Format.R8G8B8A8Snorm },
- { VertexAttributeFormat.R8G8B8A8Uint, Format.R8G8B8A8Uint },
- { VertexAttributeFormat.R8G8B8A8Sint, Format.R8G8B8A8Sint },
- { VertexAttributeFormat.R16G16B16A16Float, Format.R16G16B16A16Float },
- { VertexAttributeFormat.R16G16B16A16Unorm, Format.R16G16B16A16Unorm },
- { VertexAttributeFormat.R16G16B16A16Snorm, Format.R16G16B16A16Snorm },
- { VertexAttributeFormat.R16G16B16A16Uint, Format.R16G16B16A16Uint },
- { VertexAttributeFormat.R16G16B16A16Sint, Format.R16G16B16A16Sint },
- { VertexAttributeFormat.R32G32B32A32Float, Format.R32G32B32A32Float },
- { VertexAttributeFormat.R32G32B32A32Uint, Format.R32G32B32A32Uint },
- { VertexAttributeFormat.R32G32B32A32Sint, Format.R32G32B32A32Sint },
- { VertexAttributeFormat.A2B10G10R10Unorm, Format.R10G10B10A2Unorm },
- { VertexAttributeFormat.A2B10G10R10Uint, Format.R10G10B10A2Uint },
- { VertexAttributeFormat.B10G11R11Float, Format.R11G11B10Float },
- { VertexAttributeFormat.R8Uscaled, Format.R8Uscaled },
- { VertexAttributeFormat.R8Sscaled, Format.R8Sscaled },
- { VertexAttributeFormat.R16Uscaled, Format.R16Uscaled },
- { VertexAttributeFormat.R16Sscaled, Format.R16Sscaled },
- { VertexAttributeFormat.R32Uscaled, Format.R32Uscaled },
- { VertexAttributeFormat.R32Sscaled, Format.R32Sscaled },
- { VertexAttributeFormat.R8G8Uscaled, Format.R8G8Uscaled },
- { VertexAttributeFormat.R8G8Sscaled, Format.R8G8Sscaled },
- { VertexAttributeFormat.R16G16Uscaled, Format.R16G16Uscaled },
- { VertexAttributeFormat.R16G16Sscaled, Format.R16G16Sscaled },
- { VertexAttributeFormat.R32G32Uscaled, Format.R32G32Uscaled },
- { VertexAttributeFormat.R32G32Sscaled, Format.R32G32Sscaled },
- { VertexAttributeFormat.R8G8B8Uscaled, Format.R8G8B8Uscaled },
- { VertexAttributeFormat.R8G8B8Sscaled, Format.R8G8B8Sscaled },
- { VertexAttributeFormat.R16G16B16Uscaled, Format.R16G16B16Uscaled },
- { VertexAttributeFormat.R16G16B16Sscaled, Format.R16G16B16Sscaled },
- { VertexAttributeFormat.R32G32B32Uscaled, Format.R32G32B32Uscaled },
- { VertexAttributeFormat.R32G32B32Sscaled, Format.R32G32B32Sscaled },
- { VertexAttributeFormat.R8G8B8A8Uscaled, Format.R8G8B8A8Uscaled },
- { VertexAttributeFormat.R8G8B8A8Sscaled, Format.R8G8B8A8Sscaled },
- { VertexAttributeFormat.R16G16B16A16Uscaled, Format.R16G16B16A16Uscaled },
- { VertexAttributeFormat.R16G16B16A16Sscaled, Format.R16G16B16A16Sscaled },
- { VertexAttributeFormat.R32G32B32A32Uscaled, Format.R32G32B32A32Uscaled },
- { VertexAttributeFormat.R32G32B32A32Sscaled, Format.R32G32B32A32Sscaled },
- { VertexAttributeFormat.A2B10G10R10Snorm, Format.R10G10B10A2Snorm },
- { VertexAttributeFormat.A2B10G10R10Sint, Format.R10G10B10A2Sint },
- { VertexAttributeFormat.A2B10G10R10Uscaled, Format.R10G10B10A2Uscaled },
- { VertexAttributeFormat.A2B10G10R10Sscaled, Format.R10G10B10A2Sscaled }
- };
-
- /// <summary>
- /// Try getting the texture format from an encoded format integer from the Maxwell texture descriptor.
- /// </summary>
- /// <param name="encoded">The encoded format integer from the texture descriptor</param>
- /// <param name="isSrgb">Indicates if the format is a sRGB format</param>
- /// <param name="format">The output texture format</param>
- /// <returns>True if the format is valid, false otherwise</returns>
- public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
- {
- encoded |= (isSrgb ? 1u << 19 : 0u);
-
- return _textureFormats.TryGetValue((TextureFormat)encoded, out format);
- }
-
- /// <summary>
- /// Try getting the vertex attribute format from an encoded format integer from Maxwell attribute registers.
- /// </summary>
- /// <param name="encoded">The encoded format integer from the attribute registers</param>
- /// <param name="format">The output vertex attribute format</param>
- /// <returns>True if the format is valid, false otherwise</returns>
- public static bool TryGetAttribFormat(uint encoded, out Format format)
- {
- return _attribFormats.TryGetValue((VertexAttributeFormat)encoded, out format);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/ITextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/ITextureDescriptor.cs
deleted file mode 100644
index 378de44b..00000000
--- a/Ryujinx.Graphics.Gpu/Image/ITextureDescriptor.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- interface ITextureDescriptor
- {
- public uint UnpackFormat();
- public TextureTarget UnpackTextureTarget();
- public bool UnpackSrgb();
- public bool UnpackTextureCoordNormalized();
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs
deleted file mode 100644
index 3e557c0b..00000000
--- a/Ryujinx.Graphics.Gpu/Image/Pool.cs
+++ /dev/null
@@ -1,222 +0,0 @@
-using Ryujinx.Cpu.Tracking;
-using Ryujinx.Graphics.Gpu.Memory;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Represents a pool of GPU resources, such as samplers or textures.
- /// </summary>
- /// <typeparam name="T1">Type of the GPU resource</typeparam>
- /// <typeparam name="T2">Type of the descriptor</typeparam>
- abstract class Pool<T1, T2> : IDisposable where T2 : unmanaged
- {
- protected const int DescriptorSize = 0x20;
-
- protected GpuContext Context;
- protected PhysicalMemory PhysicalMemory;
- protected int SequenceNumber;
- protected int ModifiedSequenceNumber;
-
- protected T1[] Items;
- protected T2[] DescriptorCache;
-
- /// <summary>
- /// The maximum ID value of resources on the pool (inclusive).
- /// </summary>
- /// <remarks>
- /// The maximum amount of resources on the pool is equal to this value plus one.
- /// </remarks>
- public int MaximumId { get; }
-
- /// <summary>
- /// The address of the pool in guest memory.
- /// </summary>
- public ulong Address { get; }
-
- /// <summary>
- /// The size of the pool in bytes.
- /// </summary>
- public ulong Size { get; }
-
- private readonly CpuMultiRegionHandle _memoryTracking;
- private readonly Action<ulong, ulong> _modifiedDelegate;
-
- private int _modifiedSequenceOffset;
- private bool _modified;
-
- /// <summary>
- /// Creates a new instance of the GPU resource pool.
- /// </summary>
- /// <param name="context">GPU context that the pool belongs to</param>
- /// <param name="physicalMemory">Physical memory where the resource descriptors are mapped</param>
- /// <param name="address">Address of the pool in physical memory</param>
- /// <param name="maximumId">Maximum index of an item on the pool (inclusive)</param>
- public Pool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId)
- {
- Context = context;
- PhysicalMemory = physicalMemory;
- MaximumId = maximumId;
-
- int count = maximumId + 1;
-
- ulong size = (ulong)(uint)count * DescriptorSize;
-
- Items = new T1[count];
- DescriptorCache = new T2[count];
-
- Address = address;
- Size = size;
-
- _memoryTracking = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Pool);
- _memoryTracking.RegisterPreciseAction(address, size, PreciseAction);
- _modifiedDelegate = RegionModified;
- }
-
- /// <summary>
- /// Gets the descriptor for a given ID.
- /// </summary>
- /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
- /// <returns>The descriptor</returns>
- public T2 GetDescriptor(int id)
- {
- return PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
- }
-
- /// <summary>
- /// Gets a reference to the descriptor for a given ID.
- /// </summary>
- /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
- /// <returns>A reference to the descriptor</returns>
- public ref readonly T2 GetDescriptorRef(int id)
- {
- return ref GetDescriptorRefAddress(Address + (ulong)id * DescriptorSize);
- }
-
- /// <summary>
- /// Gets a reference to the descriptor for a given address.
- /// </summary>
- /// <param name="address">Address of the descriptor</param>
- /// <returns>A reference to the descriptor</returns>
- public ref readonly T2 GetDescriptorRefAddress(ulong address)
- {
- return ref MemoryMarshal.Cast<byte, T2>(PhysicalMemory.GetSpan(address, DescriptorSize))[0];
- }
-
- /// <summary>
- /// Gets the GPU resource with the given ID.
- /// </summary>
- /// <param name="id">ID of the resource. This is effectively a zero-based index</param>
- /// <returns>The GPU resource with the given ID</returns>
- public abstract T1 Get(int id);
-
- /// <summary>
- /// Checks if a given ID is valid and inside the range of the pool.
- /// </summary>
- /// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
- /// <returns>True if the specified ID is valid, false otherwise</returns>
- public bool IsValidId(int id)
- {
- return (uint)id <= MaximumId;
- }
-
- /// <summary>
- /// Synchronizes host memory with guest memory.
- /// This causes invalidation of pool entries,
- /// if a modification of entries by the CPU is detected.
- /// </summary>
- public void SynchronizeMemory()
- {
- _modified = false;
- _memoryTracking.QueryModified(_modifiedDelegate);
-
- if (_modified)
- {
- UpdateModifiedSequence();
- }
- }
-
- /// <summary>
- /// Indicate that a region of the pool was modified, and must be loaded from memory.
- /// </summary>
- /// <param name="mAddress">Start address of the modified region</param>
- /// <param name="mSize">Size of the modified region</param>
- private void RegionModified(ulong mAddress, ulong mSize)
- {
- _modified = true;
-
- if (mAddress < Address)
- {
- mAddress = Address;
- }
-
- ulong maxSize = Address + Size - mAddress;
-
- if (mSize > maxSize)
- {
- mSize = maxSize;
- }
-
- InvalidateRangeImpl(mAddress, mSize);
- }
-
- /// <summary>
- /// Updates the modified sequence number using the current sequence number and offset,
- /// indicating that it has been modified.
- /// </summary>
- protected void UpdateModifiedSequence()
- {
- ModifiedSequenceNumber = SequenceNumber + _modifiedSequenceOffset;
- }
-
- /// <summary>
- /// An action to be performed when a precise memory access occurs to this resource.
- /// Makes sure that the dirty flags are checked.
- /// </summary>
- /// <param name="address">Address of the memory action</param>
- /// <param name="size">Size in bytes</param>
- /// <param name="write">True if the access was a write, false otherwise</param>
- private bool PreciseAction(ulong address, ulong size, bool write)
- {
- if (write && Context.SequenceNumber == SequenceNumber)
- {
- if (ModifiedSequenceNumber == SequenceNumber + _modifiedSequenceOffset)
- {
- // The modified sequence number is offset when PreciseActions occur so that
- // users checking it will see an increment and know the pool has changed since
- // their last look, even though the main SequenceNumber has not been changed.
-
- _modifiedSequenceOffset++;
- }
-
- // Force the pool to be checked again the next time it is used.
- SequenceNumber--;
- }
-
- return false;
- }
-
- protected abstract void InvalidateRangeImpl(ulong address, ulong size);
-
- protected abstract void Delete(T1 item);
-
- /// <summary>
- /// Performs the disposal of all resources stored on the pool.
- /// It's an error to try using the pool after disposal.
- /// </summary>
- public virtual void Dispose()
- {
- if (Items != null)
- {
- for (int index = 0; index < Items.Length; index++)
- {
- Delete(Items[index]);
- }
-
- Items = null;
- }
- _memoryTracking.Dispose();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/PoolCache.cs b/Ryujinx.Graphics.Gpu/Image/PoolCache.cs
deleted file mode 100644
index e1493f38..00000000
--- a/Ryujinx.Graphics.Gpu/Image/PoolCache.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Resource pool interface.
- /// </summary>
- /// <typeparam name="T">Resource pool type</typeparam>
- interface IPool<T>
- {
- /// <summary>
- /// Start address of the pool in memory.
- /// </summary>
- ulong Address { get; }
-
- /// <summary>
- /// Linked list node used on the texture pool cache.
- /// </summary>
- LinkedListNode<T> CacheNode { get; set; }
-
- /// <summary>
- /// Timestamp set on the last use of the pool by the cache.
- /// </summary>
- ulong CacheTimestamp { get; set; }
- }
-
- /// <summary>
- /// Pool cache.
- /// This can keep multiple pools, and return the current one as needed.
- /// </summary>
- abstract class PoolCache<T> : IDisposable where T : IPool<T>, IDisposable
- {
- private const int MaxCapacity = 2;
- private const ulong MinDeltaForRemoval = 20000;
-
- private readonly GpuContext _context;
- private readonly LinkedList<T> _pools;
- private ulong _currentTimestamp;
-
- /// <summary>
- /// Constructs a new instance of the pool.
- /// </summary>
- /// <param name="context">GPU context that the texture pool belongs to</param>
- public PoolCache(GpuContext context)
- {
- _context = context;
- _pools = new LinkedList<T>();
- }
-
- /// <summary>
- /// Increments the internal timestamp of the cache that is used to decide when old resources will be deleted.
- /// </summary>
- public void Tick()
- {
- _currentTimestamp++;
- }
-
- /// <summary>
- /// Finds a cache texture pool, or creates a new one if not found.
- /// </summary>
- /// <param name="channel">GPU channel that the texture pool cache belongs to</param>
- /// <param name="address">Start address of the texture pool</param>
- /// <param name="maximumId">Maximum ID of the texture pool</param>
- /// <returns>The found or newly created texture pool</returns>
- public T FindOrCreate(GpuChannel channel, ulong address, int maximumId)
- {
- // Remove old entries from the cache, if possible.
- while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
- {
- T oldestPool = _pools.First.Value;
-
- _pools.RemoveFirst();
- oldestPool.Dispose();
- oldestPool.CacheNode = null;
- }
-
- T pool;
-
- // Try to find the pool on the cache.
- for (LinkedListNode<T> 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);
- }
-
- pool.CacheTimestamp = _currentTimestamp;
-
- return pool;
- }
- }
-
- // If not found, create a new one.
- pool = CreatePool(_context, channel, address, maximumId);
-
- pool.CacheNode = _pools.AddLast(pool);
- pool.CacheTimestamp = _currentTimestamp;
-
- return pool;
- }
-
- /// <summary>
- /// Creates a new instance of the pool.
- /// </summary>
- /// <param name="context">GPU context that the pool belongs to</param>
- /// <param name="channel">GPU channel that the pool belongs to</param>
- /// <param name="address">Address of the pool in guest memory</param>
- /// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
- protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId);
-
- public void Dispose()
- {
- foreach (T pool in _pools)
- {
- pool.Dispose();
- pool.CacheNode = null;
- }
-
- _pools.Clear();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs b/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
deleted file mode 100644
index 1f7d9b07..00000000
--- a/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Represents a filter used with texture minification linear filtering.
- /// </summary>
- /// <remarks>
- /// This feature is only supported on NVIDIA GPUs.
- /// </remarks>
- enum ReductionFilter
- {
- Average,
- Minimum,
- Maximum
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/Ryujinx.Graphics.Gpu/Image/Sampler.cs
deleted file mode 100644
index b70ac9eb..00000000
--- a/Ryujinx.Graphics.Gpu/Image/Sampler.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using System;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Cached sampler entry for sampler pools.
- /// </summary>
- class Sampler : IDisposable
- {
- /// <summary>
- /// True if the sampler is disposed, false otherwise.
- /// </summary>
- public bool IsDisposed { get; private set; }
-
- /// <summary>
- /// Host sampler object.
- /// </summary>
- private readonly ISampler _hostSampler;
-
- /// <summary>
- /// Host sampler object, with anisotropy forced.
- /// </summary>
- private readonly ISampler _anisoSampler;
-
- /// <summary>
- /// Creates a new instance of the cached sampler.
- /// </summary>
- /// <param name="context">The GPU context the sampler belongs to</param>
- /// <param name="descriptor">The Maxwell sampler descriptor</param>
- public Sampler(GpuContext context, SamplerDescriptor descriptor)
- {
- MinFilter minFilter = descriptor.UnpackMinFilter();
- MagFilter magFilter = descriptor.UnpackMagFilter();
-
- bool seamlessCubemap = descriptor.UnpackSeamlessCubemap();
-
- AddressMode addressU = descriptor.UnpackAddressU();
- AddressMode addressV = descriptor.UnpackAddressV();
- AddressMode addressP = descriptor.UnpackAddressP();
-
- CompareMode compareMode = descriptor.UnpackCompareMode();
- CompareOp compareOp = descriptor.UnpackCompareOp();
-
- ColorF color = new ColorF(
- descriptor.BorderColorR,
- descriptor.BorderColorG,
- descriptor.BorderColorB,
- descriptor.BorderColorA);
-
- float minLod = descriptor.UnpackMinLod();
- float maxLod = descriptor.UnpackMaxLod();
- float mipLodBias = descriptor.UnpackMipLodBias();
-
- float maxRequestedAnisotropy = descriptor.UnpackMaxAnisotropy();
- float maxSupportedAnisotropy = context.Capabilities.MaximumSupportedAnisotropy;
-
- _hostSampler = context.Renderer.CreateSampler(new SamplerCreateInfo(
- minFilter,
- magFilter,
- seamlessCubemap,
- addressU,
- addressV,
- addressP,
- compareMode,
- compareOp,
- color,
- minLod,
- maxLod,
- mipLodBias,
- Math.Min(maxRequestedAnisotropy, maxSupportedAnisotropy)));
-
- if (GraphicsConfig.MaxAnisotropy >= 0 && GraphicsConfig.MaxAnisotropy <= 16 && (minFilter == MinFilter.LinearMipmapNearest || minFilter == MinFilter.LinearMipmapLinear))
- {
- maxRequestedAnisotropy = GraphicsConfig.MaxAnisotropy;
-
- _anisoSampler = context.Renderer.CreateSampler(new SamplerCreateInfo(
- minFilter,
- magFilter,
- seamlessCubemap,
- addressU,
- addressV,
- addressP,
- compareMode,
- compareOp,
- color,
- minLod,
- maxLod,
- mipLodBias,
- Math.Min(maxRequestedAnisotropy, maxSupportedAnisotropy)));
- }
- }
-
- /// <summary>
- /// Gets a host sampler for the given texture.
- /// </summary>
- /// <param name="texture">Texture to be sampled</param>
- /// <returns>A host sampler</returns>
- public ISampler GetHostSampler(Texture texture)
- {
- return _anisoSampler != null && texture?.CanForceAnisotropy == true ? _anisoSampler : _hostSampler;
- }
-
- /// <summary>
- /// Disposes the host sampler object.
- /// </summary>
- public void Dispose()
- {
- IsDisposed = true;
-
- _hostSampler.Dispose();
- _anisoSampler?.Dispose();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
deleted file mode 100644
index 64a146fb..00000000
--- a/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
+++ /dev/null
@@ -1,260 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using System.Runtime.CompilerServices;
-using System.Runtime.Intrinsics;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Maxwell sampler descriptor structure.
- /// This structure defines the sampler descriptor as it is packed on the GPU sampler pool region.
- /// </summary>
- 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;
-
-#pragma warning disable CS0649
- public uint Word0;
- public uint Word1;
- public uint Word2;
- public uint Word3;
- public float BorderColorR;
- public float BorderColorG;
- public float BorderColorB;
- public float BorderColorA;
-#pragma warning restore CS0649
-
- /// <summary>
- /// Unpacks the texture wrap mode along the X axis.
- /// </summary>
- /// <returns>The texture wrap mode enum</returns>
- public AddressMode UnpackAddressU()
- {
- return (AddressMode)(Word0 & 7);
- }
-
- // <summary>
- /// Unpacks the texture wrap mode along the Y axis.
- /// </summary>
- /// <returns>The texture wrap mode enum</returns>
- public AddressMode UnpackAddressV()
- {
- return (AddressMode)((Word0 >> 3) & 7);
- }
-
- // <summary>
- /// Unpacks the texture wrap mode along the Z axis.
- /// </summary>
- /// <returns>The texture wrap mode enum</returns>
- public AddressMode UnpackAddressP()
- {
- return (AddressMode)((Word0 >> 6) & 7);
- }
-
- /// <summary>
- /// Unpacks the compare mode used for depth comparison on the shader, for
- /// depth buffer texture.
- /// This is only relevant for shaders with shadow samplers.
- /// </summary>
- /// <returns>The depth comparison mode enum</returns>
- public CompareMode UnpackCompareMode()
- {
- return (CompareMode)((Word0 >> 9) & 1);
- }
-
- /// <summary>
- /// Unpacks the compare operation used for depth comparison on the shader, for
- /// depth buffer texture.
- /// This is only relevant for shaders with shadow samplers.
- /// </summary>
- /// <returns>The depth comparison operation enum</returns>
- public CompareOp UnpackCompareOp()
- {
- return (CompareOp)(((Word0 >> 10) & 7) + 1);
- }
-
- /// <summary>
- /// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering.
- /// </summary>
- /// <returns>The maximum anisotropy</returns>
- public float UnpackMaxAnisotropy()
- {
- return _maxAnisotropyLut[(Word0 >> 20) & 7];
- }
-
- /// <summary>
- /// Unpacks the texture magnification filter.
- /// This defines the filtering used when the texture covers an area on the screen
- /// that is larger than the texture size.
- /// </summary>
- /// <returns>The magnification filter</returns>
- public MagFilter UnpackMagFilter()
- {
- return (MagFilter)(Word1 & 3);
- }
-
- /// <summary>
- /// Unpacks the texture minification filter.
- /// This defines the filtering used when the texture covers an area on the screen
- /// that is smaller than the texture size.
- /// </summary>
- /// <returns>The minification filter</returns>
- public MinFilter UnpackMinFilter()
- {
- SamplerMinFilter minFilter = (SamplerMinFilter)((Word1 >> 4) & 3);
- SamplerMipFilter mipFilter = (SamplerMipFilter)((Word1 >> 6) & 3);
-
- return ConvertFilter(minFilter, mipFilter);
- }
-
- /// <summary>
- /// Converts two minification and filter enum, to a single minification enum,
- /// including mipmap filtering information, as expected from the host API.
- /// </summary>
- /// <param name="minFilter">The minification filter</param>
- /// <param name="mipFilter">The mipmap level filter</param>
- /// <returns>The combined, host API compatible filter enum</returns>
- private static MinFilter ConvertFilter(SamplerMinFilter minFilter, SamplerMipFilter mipFilter)
- {
- switch (mipFilter)
- {
- case SamplerMipFilter.None:
- switch (minFilter)
- {
- case SamplerMinFilter.Nearest: return MinFilter.Nearest;
- case SamplerMinFilter.Linear: return MinFilter.Linear;
- }
- break;
-
- case SamplerMipFilter.Nearest:
- switch (minFilter)
- {
- case SamplerMinFilter.Nearest: return MinFilter.NearestMipmapNearest;
- case SamplerMinFilter.Linear: return MinFilter.LinearMipmapNearest;
- }
- break;
-
- case SamplerMipFilter.Linear:
- switch (minFilter)
- {
- case SamplerMinFilter.Nearest: return MinFilter.NearestMipmapLinear;
- case SamplerMinFilter.Linear: return MinFilter.LinearMipmapLinear;
- }
- break;
- }
-
- return MinFilter.Nearest;
- }
-
- /// <summary>
- /// Unpacks the seamless cubemap flag.
- /// </summary>
- /// <returns>The seamless cubemap flag</returns>
- public bool UnpackSeamlessCubemap()
- {
- return (Word1 & (1 << 9)) != 0;
- }
-
- /// <summary>
- /// Unpacks the reduction filter, used with texture minification linear filtering.
- /// This describes how the final value will be computed from neighbouring pixels.
- /// </summary>
- /// <returns>The reduction filter</returns>
- public ReductionFilter UnpackReductionFilter()
- {
- return (ReductionFilter)((Word1 >> 10) & 3);
- }
-
- /// <summary>
- /// Unpacks the level-of-detail bias value.
- /// This is a bias added to the level-of-detail value as computed by the GPU, used to select
- /// which mipmap level to use from a given texture.
- /// </summary>
- /// <returns>The level-of-detail bias value</returns>
- public float UnpackMipLodBias()
- {
- int fixedValue = (int)(Word1 >> 12) & 0x1fff;
-
- fixedValue = (fixedValue << 19) >> 19;
-
- return fixedValue * Frac8ToF32;
- }
-
- /// <summary>
- /// Unpacks the level-of-detail snap value.
- /// </summary>
- /// <returns>The level-of-detail snap value</returns>
- public float UnpackLodSnap()
- {
- return _f5ToF32ConversionLut[(Word1 >> 26) & 0x1f];
- }
-
- /// <summary>
- /// Unpacks the minimum level-of-detail value.
- /// </summary>
- /// <returns>The minimum level-of-detail value</returns>
- public float UnpackMinLod()
- {
- return (Word2 & 0xfff) * Frac8ToF32;
- }
-
- /// <summary>
- /// Unpacks the maximum level-of-detail value.
- /// </summary>
- /// <returns>The maximum level-of-detail value</returns>
- public float UnpackMaxLod()
- {
- return ((Word2 >> 12) & 0xfff) * Frac8ToF32;
- }
-
- /// <summary>
- /// Check if two descriptors are equal.
- /// </summary>
- /// <param name="other">The descriptor to compare against</param>
- /// <returns>True if they are equal, false otherwise</returns>
- public bool Equals(ref SamplerDescriptor other)
- {
- return Unsafe.As<SamplerDescriptor, Vector256<byte>>(ref this).Equals(Unsafe.As<SamplerDescriptor, Vector256<byte>>(ref other));
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs b/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs
deleted file mode 100644
index 17beb129..00000000
--- a/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Sampler texture minification filter.
- /// </summary>
- enum SamplerMinFilter
- {
- Nearest = 1,
- Linear
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs b/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs
deleted file mode 100644
index 319d4196..00000000
--- a/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Sampler texture mipmap level filter.
- /// </summary>
- enum SamplerMipFilter
- {
- None = 1,
- Nearest,
- Linear
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
deleted file mode 100644
index eb7222f9..00000000
--- a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-using Ryujinx.Graphics.Gpu.Memory;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Sampler pool.
- /// </summary>
- class SamplerPool : Pool<Sampler, SamplerDescriptor>, IPool<SamplerPool>
- {
- private float _forcedAnisotropy;
-
- /// <summary>
- /// Linked list node used on the sampler pool cache.
- /// </summary>
- public LinkedListNode<SamplerPool> CacheNode { get; set; }
-
- /// <summary>
- /// Timestamp used by the sampler pool cache, updated on every use of this sampler pool.
- /// </summary>
- public ulong CacheTimestamp { get; set; }
-
- /// <summary>
- /// Creates a new instance of the sampler pool.
- /// </summary>
- /// <param name="context">GPU context that the sampler pool belongs to</param>
- /// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param>
- /// <param name="address">Address of the sampler pool in guest memory</param>
- /// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
- public SamplerPool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId)
- {
- _forcedAnisotropy = GraphicsConfig.MaxAnisotropy;
- }
-
- /// <summary>
- /// Gets the sampler with the given ID.
- /// </summary>
- /// <param name="id">ID of the sampler. This is effectively a zero-based index</param>
- /// <returns>The sampler with the given ID</returns>
- public override Sampler Get(int id)
- {
- if ((uint)id >= Items.Length)
- {
- return null;
- }
-
- if (SequenceNumber != Context.SequenceNumber)
- {
- if (_forcedAnisotropy != GraphicsConfig.MaxAnisotropy)
- {
- _forcedAnisotropy = GraphicsConfig.MaxAnisotropy;
-
- for (int i = 0; i < Items.Length; i++)
- {
- if (Items[i] != null)
- {
- Items[i].Dispose();
-
- Items[i] = null;
- }
- }
-
- UpdateModifiedSequence();
- }
-
- SequenceNumber = Context.SequenceNumber;
-
- SynchronizeMemory();
- }
-
- Sampler sampler = Items[id];
-
- if (sampler == null)
- {
- SamplerDescriptor descriptor = GetDescriptor(id);
-
- sampler = new Sampler(Context, descriptor);
-
- Items[id] = sampler;
-
- DescriptorCache[id] = descriptor;
- }
-
- return sampler;
- }
-
- /// <summary>
- /// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
- /// </summary>
- /// <returns>A number that increments each time a modification is detected</returns>
- public int CheckModified()
- {
- if (SequenceNumber != Context.SequenceNumber)
- {
- SequenceNumber = Context.SequenceNumber;
-
- if (_forcedAnisotropy != GraphicsConfig.MaxAnisotropy)
- {
- _forcedAnisotropy = GraphicsConfig.MaxAnisotropy;
-
- for (int i = 0; i < Items.Length; i++)
- {
- if (Items[i] != null)
- {
- Items[i].Dispose();
-
- Items[i] = null;
- }
- }
-
- UpdateModifiedSequence();
- }
-
- SynchronizeMemory();
- }
-
- return ModifiedSequenceNumber;
- }
-
- /// <summary>
- /// Implementation of the sampler pool range invalidation.
- /// </summary>
- /// <param name="address">Start address of the range of the sampler pool</param>
- /// <param name="size">Size of the range being invalidated</param>
- protected override void InvalidateRangeImpl(ulong address, ulong size)
- {
- ulong endAddress = address + size;
-
- for (; address < endAddress; address += DescriptorSize)
- {
- int id = (int)((address - Address) / DescriptorSize);
-
- Sampler sampler = Items[id];
-
- if (sampler != null)
- {
- SamplerDescriptor descriptor = GetDescriptor(id);
-
- // If the descriptors are the same, the sampler is still valid.
- if (descriptor.Equals(ref DescriptorCache[id]))
- {
- continue;
- }
-
- sampler.Dispose();
-
- Items[id] = null;
- }
- }
- }
-
- /// <summary>
- /// Deletes a given sampler pool entry.
- /// The host memory used by the sampler is released by the driver.
- /// </summary>
- /// <param name="item">The entry to be deleted</param>
- protected override void Delete(Sampler item)
- {
- item?.Dispose();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs
deleted file mode 100644
index 3b3350fb..00000000
--- a/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Sampler pool cache.
- /// This can keep multiple sampler pools, and return the current one as needed.
- /// It is useful for applications that uses multiple sampler pools.
- /// </summary>
- class SamplerPoolCache : PoolCache<SamplerPool>
- {
- /// <summary>
- /// Constructs a new instance of the texture pool.
- /// </summary>
- /// <param name="context">GPU context that the texture pool belongs to</param>
- public SamplerPoolCache(GpuContext context) : base(context)
- {
- }
-
- /// <summary>
- /// Creates a new instance of the sampler pool.
- /// </summary>
- /// <param name="context">GPU context that the sampler pool belongs to</param>
- /// <param name="channel">GPU channel that the texture pool belongs to</param>
- /// <param name="address">Address of the sampler pool in guest memory</param>
- /// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
- protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
- {
- return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
deleted file mode 100644
index 84808a84..00000000
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ /dev/null
@@ -1,1705 +0,0 @@
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Memory;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Texture;
-using Ryujinx.Graphics.Texture.Astc;
-using Ryujinx.Memory;
-using Ryujinx.Memory.Range;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Numerics;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Represents a cached GPU texture.
- /// </summary>
- class Texture : IMultiRangeItem, IDisposable
- {
- // How many updates we need before switching to the byte-by-byte comparison
- // modification check method.
- // This method uses much more memory so we want to avoid it if possible.
- private const int ByteComparisonSwitchThreshold = 4;
-
- // Tuning for blacklisting textures from scaling when their data is updated from CPU.
- // Each write adds the weight, each GPU modification subtracts 1.
- // Exceeding the threshold blacklists the texture.
- private const int ScaledSetWeight = 10;
- private const int ScaledSetThreshold = 30;
-
- private const int MinLevelsForForceAnisotropy = 5;
-
- private struct TexturePoolOwner
- {
- public TexturePool Pool;
- public int ID;
- public ulong GpuAddress;
- }
-
- private GpuContext _context;
- private PhysicalMemory _physicalMemory;
-
- private SizeInfo _sizeInfo;
-
- /// <summary>
- /// Texture format.
- /// </summary>
- public Format Format => Info.FormatInfo.Format;
-
- /// <summary>
- /// Texture target.
- /// </summary>
- public Target Target { get; private set; }
-
- /// <summary>
- /// Texture width.
- /// </summary>
- public int Width { get; private set; }
-
- /// <summary>
- /// Texture height.
- /// </summary>
- public int Height { get; private set; }
-
- /// <summary>
- /// Texture information.
- /// </summary>
- public TextureInfo Info { get; private set; }
-
- /// <summary>
- /// Set when anisotropic filtering can be forced on the given texture.
- /// </summary>
- public bool CanForceAnisotropy { get; private set; }
-
- /// <summary>
- /// Host scale factor.
- /// </summary>
- public float ScaleFactor { get; private set; }
-
- /// <summary>
- /// Upscaling mode. Informs if a texture is scaled, or is eligible for scaling.
- /// </summary>
- public TextureScaleMode ScaleMode { get; private set; }
-
- /// <summary>
- /// Group that this texture belongs to. Manages read/write memory tracking.
- /// </summary>
- public TextureGroup Group { get; private set; }
-
- /// <summary>
- /// Set when a texture's GPU VA has ever been partially or fully unmapped.
- /// This indicates that the range must be fully checked when matching the texture.
- /// </summary>
- public bool ChangedMapping { get; private set; }
-
- /// <summary>
- /// True if the data for this texture must always be flushed when an overlap appears.
- /// This is useful if SetData is called directly on this texture, but the data is meant for a future texture.
- /// </summary>
- public bool AlwaysFlushOnOverlap { get; private set; }
-
- /// <summary>
- /// Increments when the host texture is swapped, or when the texture is removed from all pools.
- /// </summary>
- public int InvalidatedSequence { get; private set; }
-
- private int _depth;
- private int _layers;
- public int FirstLayer { get; private set; }
- public int FirstLevel { get; private set; }
-
- private bool _hasData;
- private bool _dirty = true;
- private int _updateCount;
- private byte[] _currentData;
-
- private bool _modifiedStale = true;
-
- private ITexture _arrayViewTexture;
- private Target _arrayViewTarget;
-
- private ITexture _flushHostTexture;
- private ITexture _setHostTexture;
- private int _scaledSetScore;
-
- private Texture _viewStorage;
-
- private List<Texture> _views;
-
- /// <summary>
- /// Host texture.
- /// </summary>
- public ITexture HostTexture { get; private set; }
-
- /// <summary>
- /// Intrusive linked list node used on the auto deletion texture cache.
- /// </summary>
- public LinkedListNode<Texture> CacheNode { get; set; }
-
- /// <summary>
- /// Entry for this texture in the short duration cache, if present.
- /// </summary>
- public ShortTextureCacheEntry ShortCacheEntry { get; set; }
-
- /// Physical memory ranges where the texture data is located.
- /// </summary>
- public MultiRange Range { get; private set; }
-
- /// <summary>
- /// Layer size in bytes.
- /// </summary>
- public int LayerSize => _sizeInfo.LayerSize;
-
- /// <summary>
- /// Texture size in bytes.
- /// </summary>
- public ulong Size => (ulong)_sizeInfo.TotalSize;
-
- /// <summary>
- /// Whether or not the texture belongs is a view.
- /// </summary>
- public bool IsView => _viewStorage != this;
-
- /// <summary>
- /// Whether or not this texture has views.
- /// </summary>
- public bool HasViews => _views.Count > 0;
-
- private int _referenceCount;
- private List<TexturePoolOwner> _poolOwners;
-
- /// <summary>
- /// Constructs a new instance of the cached GPU texture.
- /// </summary>
- /// <param name="context">GPU context that the texture belongs to</param>
- /// <param name="physicalMemory">Physical memory where the texture is mapped</param>
- /// <param name="info">Texture information</param>
- /// <param name="sizeInfo">Size information of the texture</param>
- /// <param name="range">Physical memory ranges where the texture data is located</param>
- /// <param name="firstLayer">The first layer of the texture, or 0 if the texture has no parent</param>
- /// <param name="firstLevel">The first mipmap level of the texture, or 0 if the texture has no parent</param>
- /// <param name="scaleFactor">The floating point scale factor to initialize with</param>
- /// <param name="scaleMode">The scale mode to initialize with</param>
- private Texture(
- GpuContext context,
- PhysicalMemory physicalMemory,
- TextureInfo info,
- SizeInfo sizeInfo,
- MultiRange range,
- int firstLayer,
- int firstLevel,
- float scaleFactor,
- TextureScaleMode scaleMode)
- {
- InitializeTexture(context, physicalMemory, info, sizeInfo, range);
-
- FirstLayer = firstLayer;
- FirstLevel = firstLevel;
-
- ScaleFactor = scaleFactor;
- ScaleMode = scaleMode;
-
- InitializeData(true);
- }
-
- /// <summary>
- /// Constructs a new instance of the cached GPU texture.
- /// </summary>
- /// <param name="context">GPU context that the texture belongs to</param>
- /// <param name="physicalMemory">Physical memory where the texture is mapped</param>
- /// <param name="info">Texture information</param>
- /// <param name="sizeInfo">Size information of the texture</param>
- /// <param name="range">Physical memory ranges where the texture data is located</param>
- /// <param name="scaleMode">The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up</param>
- public Texture(
- GpuContext context,
- PhysicalMemory physicalMemory,
- TextureInfo info,
- SizeInfo sizeInfo,
- MultiRange range,
- TextureScaleMode scaleMode)
- {
- ScaleFactor = 1f; // Texture is first loaded at scale 1x.
- ScaleMode = scaleMode;
-
- InitializeTexture(context, physicalMemory, info, sizeInfo, range);
- }
-
- /// <summary>
- /// Common texture initialization method.
- /// This sets the context, info and sizeInfo fields.
- /// Other fields are initialized with their default values.
- /// </summary>
- /// <param name="context">GPU context that the texture belongs to</param>
- /// <param name="physicalMemory">Physical memory where the texture is mapped</param>
- /// <param name="info">Texture information</param>
- /// <param name="sizeInfo">Size information of the texture</param>
- /// <param name="range">Physical memory ranges where the texture data is located</param>
- private void InitializeTexture(
- GpuContext context,
- PhysicalMemory physicalMemory,
- TextureInfo info,
- SizeInfo sizeInfo,
- MultiRange range)
- {
- _context = context;
- _physicalMemory = physicalMemory;
- _sizeInfo = sizeInfo;
- Range = range;
-
- SetInfo(info);
-
- _viewStorage = this;
-
- _views = new List<Texture>();
- _poolOwners = new List<TexturePoolOwner>();
- }
-
- /// <summary>
- /// Initializes the data for a texture. Can optionally initialize the texture with or without data.
- /// If the texture is a view, it will initialize memory tracking to be non-dirty.
- /// </summary>
- /// <param name="isView">True if the texture is a view, false otherwise</param>
- /// <param name="withData">True if the texture is to be initialized with data</param>
- public void InitializeData(bool isView, bool withData = false)
- {
- withData |= Group != null && Group.FlushIncompatibleOverlapsIfNeeded();
-
- if (withData)
- {
- Debug.Assert(!isView);
-
- TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
- HostTexture = _context.Renderer.CreateTexture(createInfo, ScaleFactor);
-
- SynchronizeMemory(); // Load the data.
- if (ScaleMode == TextureScaleMode.Scaled)
- {
- SetScale(GraphicsConfig.ResScale); // Scale the data up.
- }
- }
- else
- {
- _hasData = true;
-
- if (!isView)
- {
- // Don't update this texture the next time we synchronize.
- CheckModified(true);
-
- if (ScaleMode == TextureScaleMode.Scaled)
- {
- // Don't need to start at 1x as there is no data to scale, just go straight to the target scale.
- ScaleFactor = GraphicsConfig.ResScale;
- }
-
- TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
- HostTexture = _context.Renderer.CreateTexture(createInfo, ScaleFactor);
- }
- }
- }
-
- /// <summary>
- /// Initialize a new texture group with this texture as storage.
- /// </summary>
- /// <param name="hasLayerViews">True if the texture will have layer views</param>
- /// <param name="hasMipViews">True if the texture will have mip views</param>
- /// <param name="incompatibleOverlaps">Groups that overlap with this one but are incompatible</param>
- public void InitializeGroup(bool hasLayerViews, bool hasMipViews, List<TextureIncompatibleOverlap> incompatibleOverlaps)
- {
- Group = new TextureGroup(_context, _physicalMemory, this, incompatibleOverlaps);
-
- Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews);
- }
-
- /// <summary>
- /// Create a texture view from this texture.
- /// A texture view is defined as a child texture, from a sub-range of their parent texture.
- /// For example, the initial layer and mipmap level of the view can be defined, so the texture
- /// will start at the given layer/level of the parent texture.
- /// </summary>
- /// <param name="info">Child texture information</param>
- /// <param name="sizeInfo">Child texture size information</param>
- /// <param name="range">Physical memory ranges where the texture data is located</param>
- /// <param name="firstLayer">Start layer of the child texture on the parent texture</param>
- /// <param name="firstLevel">Start mipmap level of the child texture on the parent texture</param>
- /// <returns>The child texture</returns>
- public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, MultiRange range, int firstLayer, int firstLevel)
- {
- Texture texture = new Texture(
- _context,
- _physicalMemory,
- info,
- sizeInfo,
- range,
- FirstLayer + firstLayer,
- FirstLevel + firstLevel,
- ScaleFactor,
- ScaleMode);
-
- TextureCreateInfo createInfo = TextureCache.GetCreateInfo(info, _context.Capabilities, ScaleFactor);
- texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel);
-
- _viewStorage.AddView(texture);
-
- return texture;
- }
-
- /// <summary>
- /// Adds a child texture to this texture.
- /// </summary>
- /// <param name="texture">The child texture</param>
- private void AddView(Texture texture)
- {
- IncrementReferenceCount();
-
- _views.Add(texture);
-
- texture._viewStorage = this;
-
- Group.UpdateViews(_views, texture);
-
- if (texture.Group != null && texture.Group != Group)
- {
- if (texture.Group.Storage == texture)
- {
- // This texture's group is no longer used.
- Group.Inherit(texture.Group);
-
- texture.Group.Dispose();
- }
- }
-
- texture.Group = Group;
- }
-
- /// <summary>
- /// Removes a child texture from this texture.
- /// </summary>
- /// <param name="texture">The child texture</param>
- private void RemoveView(Texture texture)
- {
- _views.Remove(texture);
-
- Group.RemoveView(texture);
-
- texture._viewStorage = texture;
-
- DecrementReferenceCount();
- }
-
- /// <summary>
- /// Replaces the texture's physical memory range. This forces tracking to regenerate.
- /// </summary>
- /// <param name="range">New physical memory range backing the texture</param>
- public void ReplaceRange(MultiRange range)
- {
- Range = range;
-
- Group.RangeChanged();
- }
-
- /// <summary>
- /// Create a copy dependency to a texture that is view compatible with this one.
- /// When either texture is modified, the texture data will be copied to the other to keep them in sync.
- /// This is essentially an emulated view, useful for handling multiple view parents or format incompatibility.
- /// This also forces a copy on creation, to or from the given texture to get them in sync immediately.
- /// </summary>
- /// <param name="contained">The view compatible texture to create a dependency to</param>
- /// <param name="layer">The base layer of the given texture relative to this one</param>
- /// <param name="level">The base level of the given texture relative to this one</param>
- /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
- public void CreateCopyDependency(Texture contained, int layer, int level, bool copyTo)
- {
- if (contained.Group == Group)
- {
- return;
- }
-
- Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
- }
-
- /// <summary>
- /// Registers when a texture has had its data set after being scaled, and
- /// determines if it should be blacklisted from scaling to improve performance.
- /// </summary>
- /// <returns>True if setting data for a scaled texture is allowed, false if the texture has been blacklisted</returns>
- private bool AllowScaledSetData()
- {
- _scaledSetScore += ScaledSetWeight;
-
- if (_scaledSetScore >= ScaledSetThreshold)
- {
- BlacklistScale();
-
- return false;
- }
-
- return true;
- }
-
- /// <summary>
- /// Blacklists this texture from being scaled. Resets its scale to 1 if needed.
- /// </summary>
- public void BlacklistScale()
- {
- ScaleMode = TextureScaleMode.Blacklisted;
- SetScale(1f);
- }
-
- /// <summary>
- /// Propagates the scale between this texture and another to ensure they have the same scale.
- /// If one texture is blacklisted from scaling, the other will become blacklisted too.
- /// </summary>
- /// <param name="other">The other texture</param>
- public void PropagateScale(Texture other)
- {
- if (other.ScaleMode == TextureScaleMode.Blacklisted || ScaleMode == TextureScaleMode.Blacklisted)
- {
- BlacklistScale();
- other.BlacklistScale();
- }
- else
- {
- // Prefer the configured scale if present. If not, prefer the max.
- float targetScale = GraphicsConfig.ResScale;
- float sharedScale = (ScaleFactor == targetScale || other.ScaleFactor == targetScale) ? targetScale : Math.Max(ScaleFactor, other.ScaleFactor);
-
- SetScale(sharedScale);
- other.SetScale(sharedScale);
- }
- }
-
- /// <summary>
- /// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
- /// </summary>
- /// <param name="scale">Scale factor</param>
- /// <param name="copy">True if the data should be copied to the texture, false otherwise</param>
- /// <param name="storage">Texture to use instead of creating one</param>
- /// <returns>A host texture containing a scaled version of this texture</returns>
- private ITexture GetScaledHostTexture(float scale, bool copy, ITexture storage = null)
- {
- if (storage == null)
- {
- TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, scale);
- storage = _context.Renderer.CreateTexture(createInfo, scale);
- }
-
- if (copy)
- {
- HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
- }
-
- return storage;
- }
-
- /// <summary>
- /// Sets the Scale Factor on this texture, and immediately recreates it at the correct size.
- /// When a texture is resized, a scaled copy is performed from the old texture to the new one, to ensure no data is lost.
- /// If scale is equivalent, this only propagates the blacklisted/scaled mode.
- /// If called on a view, its storage is resized instead.
- /// When resizing storage, all texture views are recreated.
- /// </summary>
- /// <param name="scale">The new scale factor for this texture</param>
- public void SetScale(float scale)
- {
- bool unscaled = ScaleMode == TextureScaleMode.Blacklisted || (ScaleMode == TextureScaleMode.Undesired && scale == 1);
- TextureScaleMode newScaleMode = unscaled ? ScaleMode : TextureScaleMode.Scaled;
-
- if (_viewStorage != this)
- {
- _viewStorage.ScaleMode = newScaleMode;
- _viewStorage.SetScale(scale);
- return;
- }
-
- if (ScaleFactor != scale)
- {
- Logger.Debug?.Print(LogClass.Gpu, $"Rescaling {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()} to ({ScaleFactor} to {scale}). ");
-
- ScaleFactor = scale;
-
- ITexture newStorage = GetScaledHostTexture(ScaleFactor, true);
-
- Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}");
-
- ReplaceStorage(newStorage);
-
- // All views must be recreated against the new storage.
-
- foreach (var view in _views)
- {
- Logger.Debug?.Print(LogClass.Gpu, $" Recreating view {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()}.");
- view.ScaleFactor = scale;
-
- TextureCreateInfo viewCreateInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, scale);
- ITexture newView = HostTexture.CreateView(viewCreateInfo, view.FirstLayer - FirstLayer, view.FirstLevel - FirstLevel);
-
- view.ReplaceStorage(newView);
- view.ScaleMode = newScaleMode;
- }
- }
-
- if (ScaleMode != newScaleMode)
- {
- ScaleMode = newScaleMode;
-
- foreach (var view in _views)
- {
- view.ScaleMode = newScaleMode;
- }
- }
- }
-
- /// <summary>
- /// Checks if the memory for this texture was modified, and returns true if it was.
- /// The modified flags are optionally consumed as a result.
- /// </summary>
- /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
- /// <returns>True if the texture was modified, false otherwise.</returns>
- public bool CheckModified(bool consume)
- {
- return Group.CheckDirty(this, consume);
- }
-
- /// <summary>
- /// Synchronizes guest and host memory.
- /// This will overwrite the texture data with the texture data on the guest memory, if a CPU
- /// modification is detected.
- /// Be aware that this can cause texture data written by the GPU to be lost, this is just a
- /// one way copy (from CPU owned to GPU owned memory).
- /// </summary>
- public void SynchronizeMemory()
- {
- if (Target == Target.TextureBuffer)
- {
- return;
- }
-
- if (!_dirty)
- {
- return;
- }
-
- _dirty = false;
-
- if (_hasData)
- {
- Group.SynchronizeMemory(this);
- }
- else
- {
- Group.CheckDirty(this, true);
- SynchronizeFull();
- }
- }
-
- /// <summary>
- /// Signal that this texture is dirty, indicating that the texture group must be checked.
- /// </summary>
- public void SignalGroupDirty()
- {
- _dirty = true;
- }
-
- /// <summary>
- /// Signal that the modified state is dirty, indicating that the texture group should be notified when it changes.
- /// </summary>
- public void SignalModifiedDirty()
- {
- _modifiedStale = true;
- }
-
- /// <summary>
- /// Fully synchronizes guest and host memory.
- /// This will replace the entire texture with the data present in guest memory.
- /// </summary>
- public void SynchronizeFull()
- {
- ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
-
- // If the host does not support ASTC compression, we need to do the decompression.
- // The decompression is slow, so we want to avoid it as much as possible.
- // This does a byte-by-byte check and skips the update if the data is equal in this case.
- // This improves the speed on applications that overwrites ASTC data without changing anything.
- if (Info.FormatInfo.Format.IsAstc() && !_context.Capabilities.SupportsAstcCompression)
- {
- if (_updateCount < ByteComparisonSwitchThreshold)
- {
- _updateCount++;
- }
- else
- {
- bool dataMatches = _currentData != null && data.SequenceEqual(_currentData);
- if (dataMatches)
- {
- return;
- }
-
- _currentData = data.ToArray();
- }
- }
-
- SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
-
- if (ScaleFactor != 1f && AllowScaledSetData())
- {
- // If needed, create a texture to load from 1x scale.
- ITexture texture = _setHostTexture = GetScaledHostTexture(1f, false, _setHostTexture);
-
- texture.SetData(result);
-
- texture.CopyTo(HostTexture, new Extents2D(0, 0, texture.Width, texture.Height), new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), true);
- }
- else
- {
- HostTexture.SetData(result);
- }
-
- _hasData = true;
- }
-
- /// <summary>
- /// Uploads new texture data to the host GPU.
- /// </summary>
- /// <param name="data">New data</param>
- public void SetData(SpanOrArray<byte> data)
- {
- BlacklistScale();
-
- Group.CheckDirty(this, true);
-
- AlwaysFlushOnOverlap = true;
-
- HostTexture.SetData(data);
-
- _hasData = true;
- }
-
- /// <summary>
- /// Uploads new texture data to the host GPU for a specific layer/level.
- /// </summary>
- /// <param name="data">New data</param>
- /// <param name="layer">Target layer</param>
- /// <param name="level">Target level</param>
- public void SetData(SpanOrArray<byte> data, int layer, int level)
- {
- BlacklistScale();
-
- HostTexture.SetData(data, layer, level);
-
- _currentData = null;
-
- _hasData = true;
- }
-
- /// <summary>
- /// Uploads new texture data to the host GPU for a specific layer/level and 2D sub-region.
- /// </summary>
- /// <param name="data">New data</param>
- /// <param name="layer">Target layer</param>
- /// <param name="level">Target level</param>
- /// <param name="region">Target sub-region of the texture to update</param>
- public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
- {
- BlacklistScale();
-
- HostTexture.SetData(data, layer, level, region);
-
- _currentData = null;
-
- _hasData = true;
- }
-
- /// <summary>
- /// Converts texture data to a format and layout that is supported by the host GPU.
- /// </summary>
- /// <param name="data">Data to be converted</param>
- /// <param name="level">Mip level to convert</param>
- /// <param name="single">True to convert a single slice</param>
- /// <returns>Converted data</returns>
- public SpanOrArray<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
- {
- int width = Info.Width;
- int height = Info.Height;
-
- int depth = _depth;
- int layers = single ? 1 : _layers;
- int levels = single ? 1 : (Info.Levels - level);
-
- width = Math.Max(width >> level, 1);
- height = Math.Max(height >> level, 1);
- depth = Math.Max(depth >> level, 1);
-
- int sliceDepth = single ? 1 : depth;
-
- SpanOrArray<byte> result;
-
- if (Info.IsLinear)
- {
- result = LayoutConverter.ConvertLinearStridedToLinear(
- width,
- height,
- Info.FormatInfo.BlockWidth,
- Info.FormatInfo.BlockHeight,
- Info.Stride,
- Info.Stride,
- Info.FormatInfo.BytesPerPixel,
- data);
- }
- else
- {
- result = LayoutConverter.ConvertBlockLinearToLinear(
- width,
- height,
- depth,
- sliceDepth,
- levels,
- layers,
- Info.FormatInfo.BlockWidth,
- Info.FormatInfo.BlockHeight,
- Info.FormatInfo.BytesPerPixel,
- Info.GobBlocksInY,
- Info.GobBlocksInZ,
- Info.GobBlocksInTileX,
- _sizeInfo,
- data);
- }
-
- // Handle compressed cases not supported by the host:
- // - ASTC is usually not supported on desktop cards.
- // - BC4/BC5 is not supported on 3D textures.
- if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
- {
- if (!AstcDecoder.TryDecodeToRgba8P(
- result.ToArray(),
- Info.FormatInfo.BlockWidth,
- Info.FormatInfo.BlockHeight,
- width,
- height,
- sliceDepth,
- levels,
- layers,
- out byte[] decoded))
- {
- string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
-
- Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.GpuAddress:X} ({texInfo}).");
- }
-
- if (GraphicsConfig.EnableTextureRecompression)
- {
- decoded = BCnEncoder.EncodeBC7(decoded, width, height, sliceDepth, levels, layers);
- }
-
- result = decoded;
- }
- else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
- {
- switch (Format)
- {
- case Format.Etc2RgbaSrgb:
- case Format.Etc2RgbaUnorm:
- result = ETC2Decoder.DecodeRgba(result, width, height, sliceDepth, levels, layers);
- break;
- case Format.Etc2RgbPtaSrgb:
- case Format.Etc2RgbPtaUnorm:
- result = ETC2Decoder.DecodePta(result, width, height, sliceDepth, levels, layers);
- break;
- case Format.Etc2RgbSrgb:
- case Format.Etc2RgbUnorm:
- result = ETC2Decoder.DecodeRgb(result, width, height, sliceDepth, levels, layers);
- break;
- }
- }
- else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
- {
- switch (Format)
- {
- case Format.Bc1RgbaSrgb:
- case Format.Bc1RgbaUnorm:
- result = BCnDecoder.DecodeBC1(result, width, height, sliceDepth, levels, layers);
- break;
- case Format.Bc2Srgb:
- case Format.Bc2Unorm:
- result = BCnDecoder.DecodeBC2(result, width, height, sliceDepth, levels, layers);
- break;
- case Format.Bc3Srgb:
- case Format.Bc3Unorm:
- result = BCnDecoder.DecodeBC3(result, width, height, sliceDepth, levels, layers);
- break;
- case Format.Bc4Snorm:
- case Format.Bc4Unorm:
- result = BCnDecoder.DecodeBC4(result, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
- break;
- case Format.Bc5Snorm:
- case Format.Bc5Unorm:
- result = BCnDecoder.DecodeBC5(result, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
- break;
- case Format.Bc6HSfloat:
- case Format.Bc6HUfloat:
- result = BCnDecoder.DecodeBC6(result, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
- break;
- case Format.Bc7Srgb:
- case Format.Bc7Unorm:
- result = BCnDecoder.DecodeBC7(result, width, height, sliceDepth, levels, layers);
- break;
- }
- }
- else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
- {
- result = PixelConverter.ConvertR4G4ToR4G4B4A4(result, width);
-
- if (!_context.Capabilities.SupportsR4G4B4A4Format)
- {
- result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
- }
- }
- else if (Format == Format.R4G4B4A4Unorm)
- {
- if (!_context.Capabilities.SupportsR4G4B4A4Format)
- {
- result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
- }
- }
- else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked())
- {
- switch (Format)
- {
- case Format.B5G6R5Unorm:
- case Format.R5G6B5Unorm:
- result = PixelConverter.ConvertR5G6B5ToR8G8B8A8(result, width);
- break;
- case Format.B5G5R5A1Unorm:
- case Format.R5G5B5X1Unorm:
- case Format.R5G5B5A1Unorm:
- result = PixelConverter.ConvertR5G5B5ToR8G8B8A8(result, width, Format == Format.R5G5B5X1Unorm);
- break;
- case Format.A1B5G5R5Unorm:
- result = PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result, width);
- break;
- case Format.R4G4B4A4Unorm:
- result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
- break;
- }
- }
-
- return result;
- }
-
- /// <summary>
- /// Converts texture data from a format and layout that is supported by the host GPU, back into the intended format on the guest GPU.
- /// </summary>
- /// <param name="output">Optional output span to convert into</param>
- /// <param name="data">Data to be converted</param>
- /// <param name="level">Mip level to convert</param>
- /// <param name="single">True to convert a single slice</param>
- /// <returns>Converted data</returns>
- public ReadOnlySpan<byte> ConvertFromHostCompatibleFormat(Span<byte> output, ReadOnlySpan<byte> data, int level = 0, bool single = false)
- {
- if (Target != Target.TextureBuffer)
- {
- int width = Info.Width;
- int height = Info.Height;
-
- int depth = _depth;
- int layers = single ? 1 : _layers;
- int levels = single ? 1 : (Info.Levels - level);
-
- width = Math.Max(width >> level, 1);
- height = Math.Max(height >> level, 1);
- depth = Math.Max(depth >> level, 1);
-
- if (Info.IsLinear)
- {
- data = LayoutConverter.ConvertLinearToLinearStrided(
- output,
- Info.Width,
- Info.Height,
- Info.FormatInfo.BlockWidth,
- Info.FormatInfo.BlockHeight,
- Info.Stride,
- Info.FormatInfo.BytesPerPixel,
- data);
- }
- else
- {
- data = LayoutConverter.ConvertLinearToBlockLinear(
- output,
- width,
- height,
- depth,
- single ? 1 : depth,
- levels,
- layers,
- Info.FormatInfo.BlockWidth,
- Info.FormatInfo.BlockHeight,
- Info.FormatInfo.BytesPerPixel,
- Info.GobBlocksInY,
- Info.GobBlocksInZ,
- Info.GobBlocksInTileX,
- _sizeInfo,
- data);
- }
- }
-
- return data;
- }
-
- /// <summary>
- /// Flushes the texture data.
- /// This causes the texture data to be written back to guest memory.
- /// If the texture was written by the GPU, this includes all modification made by the GPU
- /// up to this point.
- /// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
- /// This may cause data corruption if the memory is already being used for something else on the CPU side.
- /// </summary>
- /// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param>
- /// <returns>True if data was flushed, false otherwise</returns>
- public bool FlushModified(bool tracked = true)
- {
- return TextureCompatibility.CanTextureFlush(Info, _context.Capabilities) && Group.FlushModified(this, tracked);
- }
-
- /// <summary>
- /// Flushes the texture data.
- /// This causes the texture data to be written back to guest memory.
- /// If the texture was written by the GPU, this includes all modification made by the GPU
- /// up to this point.
- /// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
- /// This may cause data corruption if the memory is already being used for something else on the CPU side.
- /// </summary>
- /// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param>
- public void Flush(bool tracked)
- {
- if (TextureCompatibility.CanTextureFlush(Info, _context.Capabilities))
- {
- FlushTextureDataToGuest(tracked);
- }
- }
-
- /// <summary>
- /// Gets a host texture to use for flushing the texture, at 1x resolution.
- /// If the HostTexture is already at 1x resolution, it is returned directly.
- /// </summary>
- /// <returns>The host texture to flush</returns>
- public ITexture GetFlushTexture()
- {
- ITexture texture = HostTexture;
- if (ScaleFactor != 1f)
- {
- // If needed, create a texture to flush back to host at 1x scale.
- texture = _flushHostTexture = GetScaledHostTexture(1f, true, _flushHostTexture);
- }
-
- return texture;
- }
-
- /// <summary>
- /// Gets data from the host GPU, and flushes it all to guest memory.
- /// </summary>
- /// <remarks>
- /// This method should be used to retrieve data that was modified by the host GPU.
- /// This is not cheap, avoid doing that unless strictly needed.
- /// When possible, the data is written directly into guest memory, rather than copied.
- /// </remarks>
- /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
- /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
- public void FlushTextureDataToGuest(bool tracked, ITexture texture = null)
- {
- using WritableRegion region = _physicalMemory.GetWritableRegion(Range, tracked);
-
- GetTextureDataFromGpu(region.Memory.Span, tracked, texture);
- }
-
- /// <summary>
- /// Gets data from the host GPU.
- /// </summary>
- /// <remarks>
- /// This method should be used to retrieve data that was modified by the host GPU.
- /// This is not cheap, avoid doing that unless strictly needed.
- /// </remarks>
- /// <param name="output">An output span to place the texture data into</param>
- /// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
- /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
- private void GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null)
- {
- PinnedSpan<byte> data;
-
- if (texture != null)
- {
- data = texture.GetData();
- }
- else
- {
- if (blacklist)
- {
- BlacklistScale();
- data = HostTexture.GetData();
- }
- else if (ScaleFactor != 1f)
- {
- float scale = ScaleFactor;
- SetScale(1f);
- data = HostTexture.GetData();
- SetScale(scale);
- }
- else
- {
- data = HostTexture.GetData();
- }
- }
-
- ConvertFromHostCompatibleFormat(output, data.Get());
-
- data.Dispose();
- }
-
- /// <summary>
- /// Gets data from the host GPU for a single slice.
- /// </summary>
- /// <remarks>
- /// This method should be used to retrieve data that was modified by the host GPU.
- /// This is not cheap, avoid doing that unless strictly needed.
- /// </remarks>
- /// <param name="output">An output span to place the texture data into. If empty, one is generated</param>
- /// <param name="layer">The layer of the texture to flush</param>
- /// <param name="level">The level of the texture to flush</param>
- /// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
- /// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
- public void GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
- {
- PinnedSpan<byte> data;
-
- if (texture != null)
- {
- data = texture.GetData(layer, level);
- }
- else
- {
- if (blacklist)
- {
- BlacklistScale();
- data = HostTexture.GetData(layer, level);
- }
- else if (ScaleFactor != 1f)
- {
- float scale = ScaleFactor;
- SetScale(1f);
- data = HostTexture.GetData(layer, level);
- SetScale(scale);
- }
- else
- {
- data = HostTexture.GetData(layer, level);
- }
- }
-
- ConvertFromHostCompatibleFormat(output, data.Get(), level, true);
-
- data.Dispose();
- }
-
- /// <summary>
- /// This performs a strict comparison, used to check if this texture is equal to the one supplied.
- /// </summary>
- /// <param name="info">Texture information to compare against</param>
- /// <param name="flags">Comparison flags</param>
- /// <returns>A value indicating how well this texture matches the given info</returns>
- public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
- {
- bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0;
-
- TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0);
-
- if (matchQuality == TextureMatchQuality.NoMatch)
- {
- return matchQuality;
- }
-
- if (!TextureCompatibility.LayoutMatches(Info, info))
- {
- return TextureMatchQuality.NoMatch;
- }
-
- if (!TextureCompatibility.SizeMatches(Info, info, forSampler))
- {
- return TextureMatchQuality.NoMatch;
- }
-
- if ((flags & TextureSearchFlags.ForSampler) != 0)
- {
- if (!TextureCompatibility.SamplerParamsMatches(Info, info))
- {
- return TextureMatchQuality.NoMatch;
- }
- }
-
- if ((flags & TextureSearchFlags.ForCopy) != 0)
- {
- bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;
-
- if (!msTargetCompatible && !TextureCompatibility.TargetAndSamplesCompatible(Info, info))
- {
- return TextureMatchQuality.NoMatch;
- }
- }
- else if (!TextureCompatibility.TargetAndSamplesCompatible(Info, info))
- {
- return TextureMatchQuality.NoMatch;
- }
-
- return Info.Levels == info.Levels ? matchQuality : TextureMatchQuality.NoMatch;
- }
-
- /// <summary>
- /// Check if it's possible to create a view, with the given parameters, from this texture.
- /// </summary>
- /// <param name="info">Texture view information</param>
- /// <param name="range">Texture view physical memory ranges</param>
- /// <param name="exactSize">Indicates if the texture sizes must be exactly equal, or width is allowed to differ</param>
- /// <param name="layerSize">Layer size on the given texture</param>
- /// <param name="caps">Host GPU capabilities</param>
- /// <param name="firstLayer">Texture view initial layer on this texture</param>
- /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
- /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
- public TextureViewCompatibility IsViewCompatible(
- TextureInfo info,
- MultiRange range,
- bool exactSize,
- int layerSize,
- Capabilities caps,
- out int firstLayer,
- out int firstLevel)
- {
- TextureViewCompatibility result = TextureViewCompatibility.Full;
-
- result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
- if (result != TextureViewCompatibility.Incompatible)
- {
- result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps));
-
- bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
- if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
- {
- result = TextureViewCompatibility.Incompatible;
- }
-
- if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
- {
- // AMD and Intel have a bug where the view format is always ignored;
- // they use the parent format instead.
- // Create a copy dependency to avoid this issue.
-
- result = TextureViewCompatibility.CopyOnly;
- }
- }
-
- firstLayer = 0;
- firstLevel = 0;
-
- if (result == TextureViewCompatibility.Incompatible)
- {
- return TextureViewCompatibility.Incompatible;
- }
-
- int offset = Range.FindOffset(range);
-
- if (offset < 0 || !_sizeInfo.FindView(offset, out firstLayer, out firstLevel))
- {
- return TextureViewCompatibility.LayoutIncompatible;
- }
-
- if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel))
- {
- return TextureViewCompatibility.LayoutIncompatible;
- }
-
- if (info.GetSlices() > 1 && LayerSize != layerSize)
- {
- return TextureViewCompatibility.LayoutIncompatible;
- }
-
- result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, exactSize, firstLevel));
- result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
-
- return result;
- }
-
- /// <summary>
- /// Gets a texture of the specified target type from this texture.
- /// This can be used to get an array texture from a non-array texture and vice-versa.
- /// If this texture and the requested targets are equal, then this texture Host texture is returned directly.
- /// </summary>
- /// <param name="target">The desired target type</param>
- /// <returns>A view of this texture with the requested target, or null if the target is invalid for this texture</returns>
- public ITexture GetTargetTexture(Target target)
- {
- if (target == Target)
- {
- return HostTexture;
- }
-
- if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
- {
- FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info, _context.Capabilities);
-
- TextureCreateInfo createInfo = new TextureCreateInfo(
- Info.Width,
- Info.Height,
- target == Target.CubemapArray ? 6 : 1,
- Info.Levels,
- Info.Samples,
- formatInfo.BlockWidth,
- formatInfo.BlockHeight,
- formatInfo.BytesPerPixel,
- 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;
- }
-
- /// <summary>
- /// Determine if this texture can have anisotropic filtering forced.
- /// Filtered textures that we might want to force anisotropy on should have a lot of mip levels.
- /// </summary>
- /// <returns>True if anisotropic filtering can be forced, false otherwise</returns>
- private bool CanTextureForceAnisotropy()
- {
- if (!(Target == Target.Texture2D || Target == Target.Texture2DArray))
- {
- return false;
- }
-
- int maxSize = Math.Max(Info.Width, Info.Height);
- int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
-
- return Info.Levels >= Math.Min(MinLevelsForForceAnisotropy, maxLevels);
- }
-
- /// <summary>
- /// Check if this texture and the specified target have the same number of dimensions.
- /// For the purposes of this comparison, 2D and 2D Multisample textures are not considered to have
- /// the same number of dimensions. Same for Cubemap and 3D textures.
- /// </summary>
- /// <param name="target">The target to compare with</param>
- /// <returns>True if both targets have the same number of dimensions, false otherwise</returns>
- 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;
- }
-
- /// <summary>
- /// Replaces view texture information.
- /// This should only be used for child textures with a parent.
- /// </summary>
- /// <param name="parent">The parent texture</param>
- /// <param name="info">The new view texture information</param>
- /// <param name="hostTexture">The new host texture</param>
- /// <param name="firstLayer">The first layer of the view</param>
- /// <param name="firstLevel">The first level of the view</param>
- public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture, int firstLayer, int firstLevel)
- {
- IncrementReferenceCount();
- parent._viewStorage.SynchronizeMemory();
-
- // If this texture has views, they must be given to the new parent.
- if (_views.Count > 0)
- {
- Texture[] viewCopy = _views.ToArray();
-
- foreach (Texture view in viewCopy)
- {
- TextureCreateInfo createInfo = TextureCache.GetCreateInfo(view.Info, _context.Capabilities, ScaleFactor);
-
- ITexture newView = parent.HostTexture.CreateView(createInfo, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
-
- view.ReplaceView(parent, view.Info, newView, view.FirstLayer + firstLayer, view.FirstLevel + firstLevel);
- }
- }
-
- ReplaceStorage(hostTexture);
-
- if (_viewStorage != this)
- {
- _viewStorage.RemoveView(this);
- }
-
- FirstLayer = parent.FirstLayer + firstLayer;
- FirstLevel = parent.FirstLevel + firstLevel;
- parent._viewStorage.AddView(this);
-
- SetInfo(info);
- DecrementReferenceCount();
- }
-
- /// <summary>
- /// Sets the internal texture information structure.
- /// </summary>
- /// <param name="info">The new texture information</param>
- private void SetInfo(TextureInfo info)
- {
- Info = info;
- Target = info.Target;
- Width = info.Width;
- Height = info.Height;
- CanForceAnisotropy = CanTextureForceAnisotropy();
-
- _depth = info.GetDepth();
- _layers = info.GetLayers();
- }
-
- /// <summary>
- /// Signals that the texture has been modified.
- /// </summary>
- public void SignalModified()
- {
- _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
-
- if (_modifiedStale || Group.HasCopyDependencies)
- {
- _modifiedStale = false;
- Group.SignalModified(this);
- }
-
- _physicalMemory.TextureCache.Lift(this);
- }
-
- /// <summary>
- /// Signals that a texture has been bound, or has been unbound.
- /// During this time, lazy copies will not clear the dirty flag.
- /// </summary>
- /// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
- public void SignalModifying(bool bound)
- {
- if (bound)
- {
- _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
- }
-
- if (_modifiedStale || Group.HasCopyDependencies)
- {
- _modifiedStale = false;
- Group.SignalModifying(this, bound);
- }
-
- _physicalMemory.TextureCache.Lift(this);
-
- if (bound)
- {
- IncrementReferenceCount();
- }
- else
- {
- DecrementReferenceCount();
- }
- }
-
- /// <summary>
- /// Replaces the host texture, while disposing of the old one if needed.
- /// </summary>
- /// <param name="hostTexture">The new host texture</param>
- private void ReplaceStorage(ITexture hostTexture)
- {
- DisposeTextures();
-
- HostTexture = hostTexture;
- }
-
- /// <summary>
- /// Determine if any of this texture's data overlaps with another.
- /// </summary>
- /// <param name="texture">The texture to check against</param>
- /// <param name="compatibility">The view compatibility of the two textures</param>
- /// <returns>True if any slice of the textures overlap, false otherwise</returns>
- public bool DataOverlaps(Texture texture, TextureViewCompatibility compatibility)
- {
- if (compatibility == TextureViewCompatibility.LayoutIncompatible && Info.GobBlocksInZ > 1 && Info.GobBlocksInZ == texture.Info.GobBlocksInZ)
- {
- // Allow overlapping slices of layout compatible 3D textures with matching GobBlocksInZ, as they are interleaved.
- return false;
- }
-
- if (texture._sizeInfo.AllOffsets.Length == 1 && _sizeInfo.AllOffsets.Length == 1)
- {
- return Range.OverlapsWith(texture.Range);
- }
-
- MultiRange otherRange = texture.Range;
-
- IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.Slice((ulong)region.Offset, (ulong)region.Size));
- IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.Slice((ulong)region.Offset, (ulong)region.Size));
-
- foreach (MultiRange region in regions)
- {
- foreach (MultiRange otherRegion in otherRegions)
- {
- if (region.OverlapsWith(otherRegion))
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Increments the texture reference count.
- /// </summary>
- public void IncrementReferenceCount()
- {
- _referenceCount++;
- }
-
- /// <summary>
- /// Increments the reference count and records the given texture pool and ID as a pool owner.
- /// </summary>
- /// <param name="pool">The texture pool this texture has been added to</param>
- /// <param name="id">The ID of the reference to this texture in the pool</param>
- /// <param name="gpuVa">GPU VA of the pool reference</param>
- public void IncrementReferenceCount(TexturePool pool, int id, ulong gpuVa)
- {
- lock (_poolOwners)
- {
- _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id, GpuAddress = gpuVa });
- }
- _referenceCount++;
-
- if (ShortCacheEntry != null)
- {
- _physicalMemory.TextureCache.RemoveShortCache(this);
- }
- }
-
- /// <summary>
- /// Indicates that the texture has one reference left, and will delete on reference decrement.
- /// </summary>
- /// <returns>True if there is one reference remaining, false otherwise</returns>
- public bool HasOneReference()
- {
- return _referenceCount == 1;
- }
-
- /// <summary>
- /// Decrements the texture reference count.
- /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
- /// </summary>
- /// <returns>True if the texture is now referenceless, false otherwise</returns>
- public bool DecrementReferenceCount()
- {
- int newRefCount = --_referenceCount;
-
- if (newRefCount == 0)
- {
- if (_viewStorage != this)
- {
- _viewStorage.RemoveView(this);
- }
-
- _physicalMemory.TextureCache.RemoveTextureFromCache(this);
- }
-
- Debug.Assert(newRefCount >= 0);
-
- DeleteIfNotUsed();
-
- return newRefCount <= 0;
- }
-
- /// <summary>
- /// Decrements the texture reference count, also removing an associated pool owner reference.
- /// When the reference count hits zero, the texture may be deleted and can't be used anymore.
- /// </summary>
- /// <param name="pool">The texture pool this texture is being removed from</param>
- /// <param name="id">The ID of the reference to this texture in the pool</param>
- /// <returns>True if the texture is now referenceless, false otherwise</returns>
- public bool DecrementReferenceCount(TexturePool pool, int id = -1)
- {
- lock (_poolOwners)
- {
- int references = _poolOwners.RemoveAll(entry => entry.Pool == pool && entry.ID == id || id == -1);
-
- if (references == 0)
- {
- // This reference has already been removed.
- return _referenceCount <= 0;
- }
-
- Debug.Assert(references == 1);
- }
-
- return DecrementReferenceCount();
- }
-
- /// <summary>
- /// Forcibly remove this texture from all pools that reference it.
- /// </summary>
- /// <param name="deferred">Indicates if the removal is being done from another thread.</param>
- public void RemoveFromPools(bool deferred)
- {
- lock (_poolOwners)
- {
- foreach (var owner in _poolOwners)
- {
- owner.Pool.ForceRemove(this, owner.ID, deferred);
- }
-
- _poolOwners.Clear();
- }
-
- if (ShortCacheEntry != null && _context.IsGpuThread())
- {
- // If this is called from another thread (unmapped), the short cache will
- // have to remove this texture on a future tick.
-
- _physicalMemory.TextureCache.RemoveShortCache(this);
- }
-
- InvalidatedSequence++;
- }
-
- /// <summary>
- /// Queue updating texture mappings on the pool. Happens from another thread.
- /// </summary>
- public void UpdatePoolMappings()
- {
- lock (_poolOwners)
- {
- ulong address = 0;
-
- foreach (var owner in _poolOwners)
- {
- if (address == 0 || address == owner.GpuAddress)
- {
- address = owner.GpuAddress;
-
- owner.Pool.QueueUpdateMapping(this, owner.ID);
- }
- else
- {
- // If there is a different GPU VA mapping, prefer the first and delete the others.
- owner.Pool.ForceRemove(this, owner.ID, true);
- }
- }
-
- _poolOwners.Clear();
- }
-
- InvalidatedSequence++;
- }
-
- /// <summary>
- /// Delete the texture if it is not used anymore.
- /// The texture is considered unused when the reference count is zero,
- /// and it has no child views.
- /// </summary>
- private void DeleteIfNotUsed()
- {
- // We can delete the texture as long it is not being used
- // in any cache (the reference count is 0 in this case), and
- // also all views that may be created from this texture were
- // already deleted (views count is 0).
- if (_referenceCount == 0 && _views.Count == 0)
- {
- Dispose();
- }
- }
-
- /// <summary>
- /// Performs texture disposal, deleting the texture.
- /// </summary>
- private void DisposeTextures()
- {
- InvalidatedSequence++;
-
- _currentData = null;
- HostTexture.Release();
-
- _arrayViewTexture?.Release();
- _arrayViewTexture = null;
-
- _flushHostTexture?.Release();
- _flushHostTexture = null;
-
- _setHostTexture?.Release();
- _setHostTexture = null;
- }
-
- /// <summary>
- /// Called when the memory for this texture has been unmapped.
- /// Calls are from non-gpu threads.
- /// </summary>
- /// <param name="unmapRange">The range of memory being unmapped</param>
- public void Unmapped(MultiRange unmapRange)
- {
- ChangedMapping = true;
-
- if (Group.Storage == this)
- {
- Group.ClearModified(unmapRange);
- }
-
- UpdatePoolMappings();
- }
-
- /// <summary>
- /// Performs texture disposal, deleting the texture.
- /// </summary>
- public void Dispose()
- {
- DisposeTextures();
-
- if (Group.Storage == this)
- {
- Group.Dispose();
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
deleted file mode 100644
index febe508b..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Shader;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture binding information.
- /// This is used for textures that needs to be accessed from shaders.
- /// </summary>
- readonly struct TextureBindingInfo
- {
- /// <summary>
- /// Shader sampler target type.
- /// </summary>
- public Target Target { get; }
-
- /// <summary>
- /// For images, indicates the format specified on the shader.
- /// </summary>
- public Format Format { get; }
-
- /// <summary>
- /// Shader texture host binding point.
- /// </summary>
- public int Binding { get; }
-
- /// <summary>
- /// Constant buffer slot with the texture handle.
- /// </summary>
- public int CbufSlot { get; }
-
- /// <summary>
- /// Index of the texture handle on the constant buffer at slot <see cref="CbufSlot"/>.
- /// </summary>
- public int Handle { get; }
-
- /// <summary>
- /// Flags from the texture descriptor that indicate how the texture is used.
- /// </summary>
- public TextureUsageFlags Flags { get; }
-
- /// <summary>
- /// Constructs the texture binding information structure.
- /// </summary>
- /// <param name="target">The shader sampler target type</param>
- /// <param name="format">Format of the image as declared on the shader</param>
- /// <param name="binding">The shader texture binding point</param>
- /// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
- /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
- /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
- public TextureBindingInfo(Target target, Format format, int binding, int cbufSlot, int handle, TextureUsageFlags flags)
- {
- Target = target;
- Format = format;
- Binding = binding;
- CbufSlot = cbufSlot;
- Handle = handle;
- Flags = flags;
- }
-
- /// <summary>
- /// Constructs the texture binding information structure.
- /// </summary>
- /// <param name="target">The shader sampler target type</param>
- /// <param name="binding">The shader texture binding point</param>
- /// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
- /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
- /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
- public TextureBindingInfo(Target target, int binding, int cbufSlot, int handle, TextureUsageFlags flags) : this(target, (Format)0, binding, cbufSlot, handle, flags)
- {
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
deleted file mode 100644
index bbfb704d..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ /dev/null
@@ -1,882 +0,0 @@
-using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Engine.Types;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.Shader;
-using Ryujinx.Graphics.Shader;
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture bindings manager.
- /// </summary>
- class TextureBindingsManager
- {
- private const int InitialTextureStateSize = 32;
- private const int InitialImageStateSize = 8;
-
- private readonly GpuContext _context;
-
- private readonly bool _isCompute;
-
- private ulong _texturePoolGpuVa;
- private int _texturePoolMaximumId;
- private TexturePool _texturePool;
- private ulong _samplerPoolGpuVa;
- private int _samplerPoolMaximumId;
- private SamplerIndex _samplerIndex;
- private SamplerPool _samplerPool;
-
- private readonly GpuChannel _channel;
- private readonly TexturePoolCache _texturePoolCache;
- private readonly SamplerPoolCache _samplerPoolCache;
-
- private TexturePool _cachedTexturePool;
- private SamplerPool _cachedSamplerPool;
-
- private TextureBindingInfo[][] _textureBindings;
- private TextureBindingInfo[][] _imageBindings;
-
- private struct TextureState
- {
- public ITexture Texture;
- public ISampler Sampler;
-
- public int TextureHandle;
- public int SamplerHandle;
- public Format ImageFormat;
- public int InvalidatedSequence;
- public Texture CachedTexture;
- public Sampler CachedSampler;
- }
-
- private TextureState[] _textureState;
- private TextureState[] _imageState;
-
- private int _texturePoolSequence;
- private int _samplerPoolSequence;
-
- private int _textureBufferIndex;
-
- private readonly float[] _scales;
- private bool _scaleChanged;
- private int _lastFragmentTotal;
-
- /// <summary>
- /// Constructs a new instance of the texture bindings manager.
- /// </summary>
- /// <param name="context">The GPU context that the texture bindings manager belongs to</param>
- /// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
- /// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param>
- /// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param>
- /// <param name="scales">Array where the scales for the currently bound textures are stored</param>
- /// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
- public TextureBindingsManager(
- GpuContext context,
- GpuChannel channel,
- TexturePoolCache texturePoolCache,
- SamplerPoolCache samplerPoolCache,
- float[] scales,
- bool isCompute)
- {
- _context = context;
- _channel = channel;
- _texturePoolCache = texturePoolCache;
- _samplerPoolCache = samplerPoolCache;
-
- _scales = scales;
- _isCompute = isCompute;
-
- int stages = isCompute ? 1 : Constants.ShaderStages;
-
- _textureBindings = new TextureBindingInfo[stages][];
- _imageBindings = new TextureBindingInfo[stages][];
-
- _textureState = new TextureState[InitialTextureStateSize];
- _imageState = new TextureState[InitialImageStateSize];
-
- for (int stage = 0; stage < stages; stage++)
- {
- _textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize];
- _imageBindings[stage] = new TextureBindingInfo[InitialImageStateSize];
- }
- }
-
- /// <summary>
- /// Sets the texture and image bindings.
- /// </summary>
- /// <param name="bindings">Bindings for the active shader</param>
- public void SetBindings(CachedShaderBindings bindings)
- {
- _textureBindings = bindings.TextureBindings;
- _imageBindings = bindings.ImageBindings;
-
- SetMaxBindings(bindings.MaxTextureBinding, bindings.MaxImageBinding);
- }
-
- /// <summary>
- /// Sets the max binding indexes for textures and images.
- /// </summary>
- /// <param name="maxTextureBinding">The maximum texture binding</param>
- /// <param name="maxImageBinding">The maximum image binding</param>
- public void SetMaxBindings(int maxTextureBinding, int maxImageBinding)
- {
- if (maxTextureBinding >= _textureState.Length)
- {
- Array.Resize(ref _textureState, maxTextureBinding + 1);
- }
-
- if (maxImageBinding >= _imageState.Length)
- {
- Array.Resize(ref _imageState, maxImageBinding + 1);
- }
- }
-
- /// <summary>
- /// Sets the textures constant buffer index.
- /// The constant buffer specified holds the texture handles.
- /// </summary>
- /// <param name="index">Constant buffer index</param>
- public void SetTextureBufferIndex(int index)
- {
- _textureBufferIndex = index;
- }
-
- /// <summary>
- /// Sets the current texture sampler pool to be used.
- /// </summary>
- /// <param name="gpuVa">Start GPU virtual address of the pool</param>
- /// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
- /// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
- public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
- {
- _samplerPoolGpuVa = gpuVa;
- _samplerPoolMaximumId = maximumId;
- _samplerIndex = samplerIndex;
- _samplerPool = null;
- }
-
- /// <summary>
- /// Sets the current texture pool to be used.
- /// </summary>
- /// <param name="gpuVa">Start GPU virtual address of the pool</param>
- /// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
- public void SetTexturePool(ulong gpuVa, int maximumId)
- {
- _texturePoolGpuVa = gpuVa;
- _texturePoolMaximumId = maximumId;
- _texturePool = null;
- }
-
- /// <summary>
- /// Gets a texture and a sampler from their respective pools from a texture ID and a sampler ID.
- /// </summary>
- /// <param name="textureId">ID of the texture</param>
- /// <param name="samplerId">ID of the sampler</param>
- public (Texture, Sampler) GetTextureAndSampler(int textureId, int samplerId)
- {
- (TexturePool texturePool, SamplerPool samplerPool) = GetPools();
-
- return (texturePool.Get(textureId), samplerPool.Get(samplerId));
- }
-
- /// <summary>
- /// Updates the texture scale for a given texture or image.
- /// </summary>
- /// <param name="texture">Start GPU virtual address of the pool</param>
- /// <param name="usageFlags">The related texture usage flags</param>
- /// <param name="index">The texture/image binding index</param>
- /// <param name="stage">The active shader stage</param>
- /// <returns>True if the given texture has become blacklisted, indicating that its host texture may have changed.</returns>
- private bool UpdateScale(Texture texture, TextureUsageFlags usageFlags, int index, ShaderStage stage)
- {
- float result = 1f;
- bool changed = false;
-
- if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 && texture != null)
- {
- if ((usageFlags & TextureUsageFlags.ResScaleUnsupported) != 0)
- {
- changed = texture.ScaleMode != TextureScaleMode.Blacklisted;
- texture.BlacklistScale();
- }
- else
- {
- switch (stage)
- {
- case ShaderStage.Fragment:
- float scale = texture.ScaleFactor;
-
- if (scale != 1)
- {
- Texture activeTarget = _channel.TextureManager.GetAnyRenderTarget();
-
- if (activeTarget != null && (activeTarget.Info.Width / (float)texture.Info.Width) == (activeTarget.Info.Height / (float)texture.Info.Height))
- {
- // If the texture's size is a multiple of the sampler size, enable interpolation using gl_FragCoord. (helps "invent" new integer values between scaled pixels)
- result = -scale;
- break;
- }
- }
-
- result = scale;
- break;
-
- case ShaderStage.Vertex:
- int fragmentIndex = (int)ShaderStage.Fragment - 1;
- index += _textureBindings[fragmentIndex].Length + _imageBindings[fragmentIndex].Length;
-
- result = texture.ScaleFactor;
- break;
-
- case ShaderStage.Compute:
- result = texture.ScaleFactor;
- break;
- }
- }
- }
-
- if (result != _scales[index])
- {
- _scaleChanged = true;
-
- _scales[index] = result;
- }
-
- return changed;
- }
-
- /// <summary>
- /// Determines if the vertex stage requires a scale value.
- /// </summary>
- private bool VertexRequiresScale()
- {
- for (int i = 0; i < _textureBindings[0].Length; i++)
- {
- if ((_textureBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0)
- {
- return true;
- }
- }
-
- for (int i = 0; i < _imageBindings[0].Length; i++)
- {
- if ((_imageBindings[0][i].Flags & TextureUsageFlags.NeedsScaleValue) != 0)
- {
- return true;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Uploads texture and image scales to the backend when they are used.
- /// </summary>
- private void CommitRenderScale()
- {
- // Stage 0 total: Compute or Vertex.
- int total = _textureBindings[0].Length + _imageBindings[0].Length;
-
- int fragmentIndex = (int)ShaderStage.Fragment - 1;
- int fragmentTotal = _isCompute ? 0 : (_textureBindings[fragmentIndex].Length + _imageBindings[fragmentIndex].Length);
-
- if (total != 0 && fragmentTotal != _lastFragmentTotal && VertexRequiresScale())
- {
- // Must update scales in the support buffer if:
- // - Vertex stage has bindings that require scale.
- // - Fragment stage binding count has been updated since last render scale update.
-
- _scaleChanged = true;
- }
-
- if (_scaleChanged)
- {
- if (!_isCompute)
- {
- total += fragmentTotal; // Add the fragment bindings to the total.
- }
-
- _lastFragmentTotal = fragmentTotal;
-
- _context.Renderer.Pipeline.UpdateRenderScale(_scales, total, fragmentTotal);
-
- _scaleChanged = false;
- }
- }
-
- /// <summary>
- /// Ensures that the bindings are visible to the host GPU.
- /// Note: this actually performs the binding using the host graphics API.
- /// </summary>
- /// <param name="specState">Specialization state for the bound shader</param>
- /// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
- public bool CommitBindings(ShaderSpecializationState specState)
- {
- (TexturePool texturePool, SamplerPool samplerPool) = GetPools();
-
- // Check if the texture pool has been modified since bindings were last committed.
- // If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
- if (_cachedTexturePool != texturePool || _cachedSamplerPool != samplerPool)
- {
- Rebind();
-
- _cachedTexturePool = texturePool;
- _cachedSamplerPool = samplerPool;
- }
-
- bool poolModified = false;
-
- if (texturePool != null)
- {
- int texturePoolSequence = texturePool.CheckModified();
-
- if (_texturePoolSequence != texturePoolSequence)
- {
- poolModified = true;
- _texturePoolSequence = texturePoolSequence;
- }
- }
-
- if (samplerPool != null)
- {
- int samplerPoolSequence = samplerPool.CheckModified();
-
- if (_samplerPoolSequence != samplerPoolSequence)
- {
- poolModified = true;
- _samplerPoolSequence = samplerPoolSequence;
- }
- }
-
- bool specStateMatches = true;
-
- if (_isCompute)
- {
- specStateMatches &= CommitTextureBindings(texturePool, samplerPool, ShaderStage.Compute, 0, poolModified, specState);
- specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
- }
- else
- {
- for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
- {
- int stageIndex = (int)stage - 1;
-
- specStateMatches &= CommitTextureBindings(texturePool, samplerPool, stage, stageIndex, poolModified, specState);
- specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState);
- }
- }
-
- CommitRenderScale();
-
- return specStateMatches;
- }
-
- /// <summary>
- /// Fetch the constant buffers used for a texture to cache.
- /// </summary>
- /// <param name="stageIndex">Stage index of the constant buffer</param>
- /// <param name="cachedTextureBufferIndex">The currently cached texture buffer index</param>
- /// <param name="cachedSamplerBufferIndex">The currently cached sampler buffer index</param>
- /// <param name="cachedTextureBuffer">The currently cached texture buffer data</param>
- /// <param name="cachedSamplerBuffer">The currently cached sampler buffer data</param>
- /// <param name="textureBufferIndex">The new texture buffer index</param>
- /// <param name="samplerBufferIndex">The new sampler buffer index</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void UpdateCachedBuffer(
- int stageIndex,
- scoped ref int cachedTextureBufferIndex,
- scoped ref int cachedSamplerBufferIndex,
- scoped ref ReadOnlySpan<int> cachedTextureBuffer,
- scoped ref ReadOnlySpan<int> cachedSamplerBuffer,
- int textureBufferIndex,
- int samplerBufferIndex)
- {
- if (textureBufferIndex != cachedTextureBufferIndex)
- {
- ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
-
- cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
- cachedTextureBufferIndex = textureBufferIndex;
-
- if (samplerBufferIndex == textureBufferIndex)
- {
- cachedSamplerBuffer = cachedTextureBuffer;
- cachedSamplerBufferIndex = samplerBufferIndex;
- }
- }
-
- if (samplerBufferIndex != cachedSamplerBufferIndex)
- {
- ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
-
- cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
- cachedSamplerBufferIndex = samplerBufferIndex;
- }
- }
-
- /// <summary>
- /// Counts the total number of texture bindings used by all shader stages.
- /// </summary>
- /// <returns>The total amount of textures used</returns>
- private int GetTextureBindingsCount()
- {
- int count = 0;
-
- for (int i = 0; i < _textureBindings.Length; i++)
- {
- if (_textureBindings[i] != null)
- {
- count += _textureBindings[i].Length;
- }
- }
-
- return count;
- }
-
- /// <summary>
- /// Ensures that the texture bindings are visible to the host GPU.
- /// Note: this actually performs the binding using the host graphics API.
- /// </summary>
- /// <param name="texturePool">The current texture pool</param>
- /// <param name="samplerPool">The current sampler pool</param>
- /// <param name="stage">The shader stage using the textures to be bound</param>
- /// <param name="stageIndex">The stage number of the specified shader stage</param
- /// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
- /// <param name="specState">Specialization state for the bound shader</param>
- /// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
- private bool CommitTextureBindings(
- TexturePool texturePool,
- SamplerPool samplerPool,
- ShaderStage stage,
- int stageIndex,
- bool poolModified,
- ShaderSpecializationState specState)
- {
- int textureCount = _textureBindings[stageIndex].Length;
- if (textureCount == 0)
- {
- return true;
- }
-
- if (texturePool == null)
- {
- Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
- return true;
- }
-
- bool specStateMatches = true;
-
- int cachedTextureBufferIndex = -1;
- int cachedSamplerBufferIndex = -1;
- ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty;
- ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty;
-
- for (int index = 0; index < textureCount; index++)
- {
- TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index];
- TextureUsageFlags usageFlags = bindingInfo.Flags;
-
- (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
-
- UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex);
-
- int packedId = TextureHandle.ReadPackedId(bindingInfo.Handle, cachedTextureBuffer, cachedSamplerBuffer);
- int textureId = TextureHandle.UnpackTextureId(packedId);
- int samplerId;
-
- if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
- {
- samplerId = textureId;
- }
- else
- {
- samplerId = TextureHandle.UnpackSamplerId(packedId);
- }
-
- ref TextureState state = ref _textureState[bindingInfo.Binding];
-
- if (!poolModified &&
- state.TextureHandle == textureId &&
- state.SamplerHandle == samplerId &&
- state.CachedTexture != null &&
- state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence &&
- state.CachedSampler?.IsDisposed != true)
- {
- // The texture is already bound.
- state.CachedTexture.SynchronizeMemory();
-
- if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
- UpdateScale(state.CachedTexture, usageFlags, index, stage))
- {
- ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
-
- state.Texture = hostTextureRebind;
-
- _context.Renderer.Pipeline.SetTextureAndSampler(stage, bindingInfo.Binding, hostTextureRebind, state.Sampler);
- }
-
- continue;
- }
-
- state.TextureHandle = textureId;
- state.SamplerHandle = samplerId;
-
- ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture);
-
- specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
-
- Sampler sampler = samplerPool?.Get(samplerId);
-
- ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
- ISampler hostSampler = sampler?.GetHostSampler(texture);
-
- if (hostTexture != null && texture.Target == Target.TextureBuffer)
- {
- // Ensure that the buffer texture is using the correct buffer as storage.
- // Buffers are frequently re-created to accomodate larger data, so we need to re-bind
- // to ensure we're not using a old buffer that was already deleted.
- _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
-
- // Cache is not used for buffer texture, it must always rebind.
- state.CachedTexture = null;
- }
- else
- {
- bool textureOrSamplerChanged = state.Texture != hostTexture || state.Sampler != hostSampler;
-
- if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
- UpdateScale(texture, usageFlags, index, stage))
- {
- hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
- textureOrSamplerChanged = true;
- }
-
- if (textureOrSamplerChanged)
- {
- state.Texture = hostTexture;
- state.Sampler = hostSampler;
-
- _context.Renderer.Pipeline.SetTextureAndSampler(stage, bindingInfo.Binding, hostTexture, hostSampler);
- }
-
- state.CachedTexture = texture;
- state.CachedSampler = sampler;
- state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
- }
- }
-
- return specStateMatches;
- }
-
- /// <summary>
- /// Ensures that the image bindings are visible to the host GPU.
- /// Note: this actually performs the binding using the host graphics API.
- /// </summary>
- /// <param name="pool">The current texture pool</param>
- /// <param name="stage">The shader stage using the textures to be bound</param>
- /// <param name="stageIndex">The stage number of the specified shader stage</param>
- /// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
- /// <param name="specState">Specialization state for the bound shader</param>
- /// <returns>True if all bound images match the current shader specialiation state, false otherwise</returns>
- private bool CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState)
- {
- int imageCount = _imageBindings[stageIndex].Length;
- if (imageCount == 0)
- {
- return true;
- }
-
- if (pool == null)
- {
- Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses images, but texture pool was not set.");
- return true;
- }
-
- // Scales for images appear after the texture ones.
- int baseScaleIndex = _textureBindings[stageIndex].Length;
-
- int cachedTextureBufferIndex = -1;
- int cachedSamplerBufferIndex = -1;
- ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty;
- ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty;
-
- bool specStateMatches = true;
-
- for (int index = 0; index < imageCount; index++)
- {
- TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index];
- TextureUsageFlags usageFlags = bindingInfo.Flags;
- int scaleIndex = baseScaleIndex + index;
-
- (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
-
- UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex);
-
- int packedId = TextureHandle.ReadPackedId(bindingInfo.Handle, cachedTextureBuffer, cachedSamplerBuffer);
- int textureId = TextureHandle.UnpackTextureId(packedId);
-
- ref TextureState state = ref _imageState[bindingInfo.Binding];
-
- bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
-
- if (!poolModified &&
- state.TextureHandle == textureId &&
- state.CachedTexture != null &&
- state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence)
- {
- Texture cachedTexture = state.CachedTexture;
-
- // The texture is already bound.
- cachedTexture.SynchronizeMemory();
-
- if (isStore)
- {
- cachedTexture?.SignalModified();
- }
-
- Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
-
- if (state.ImageFormat != format ||
- ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
- UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage)))
- {
- ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
-
- state.Texture = hostTextureRebind;
- state.ImageFormat = format;
-
- _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTextureRebind, format);
- }
-
- continue;
- }
-
- state.TextureHandle = textureId;
-
- ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
-
- specStateMatches &= specState.MatchesImage(stage, index, descriptor);
-
- ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
-
- if (hostTexture != null && texture.Target == Target.TextureBuffer)
- {
- // Ensure that the buffer texture is using the correct buffer as storage.
- // Buffers are frequently re-created to accomodate larger data, so we need to re-bind
- // to ensure we're not using a old buffer that was already deleted.
-
- Format format = bindingInfo.Format;
-
- if (format == 0 && texture != null)
- {
- format = texture.Format;
- }
-
- _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true);
-
- // Cache is not used for buffer texture, it must always rebind.
- state.CachedTexture = null;
- }
- else
- {
- if (isStore)
- {
- texture?.SignalModified();
- }
-
- if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
- UpdateScale(texture, usageFlags, scaleIndex, stage))
- {
- hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
- }
-
- if (state.Texture != hostTexture)
- {
- state.Texture = hostTexture;
-
- Format format = bindingInfo.Format;
-
- if (format == 0 && texture != null)
- {
- format = texture.Format;
- }
-
- state.ImageFormat = format;
-
- _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
- }
-
- state.CachedTexture = texture;
- state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
- }
- }
-
- return specStateMatches;
- }
-
- /// <summary>
- /// Gets the texture descriptor for a given texture handle.
- /// </summary>
- /// <param name="poolGpuVa">GPU virtual address of the texture pool</param>
- /// <param name="bufferIndex">Index of the constant buffer with texture handles</param>
- /// <param name="maximumId">Maximum ID of the texture pool</param>
- /// <param name="stageIndex">The stage number where the texture is bound</param>
- /// <param name="handle">The texture handle</param>
- /// <param name="cbufSlot">The texture handle's constant buffer slot</param>
- /// <returns>The texture descriptor for the specified texture</returns>
- public TextureDescriptor GetTextureDescriptor(
- ulong poolGpuVa,
- int bufferIndex,
- int maximumId,
- int stageIndex,
- int handle,
- int cbufSlot)
- {
- (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(cbufSlot, bufferIndex);
-
- int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
- int textureId = TextureHandle.UnpackTextureId(packedId);
-
- ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
-
- TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId);
-
- TextureDescriptor descriptor;
-
- if (texturePool.IsValidId(textureId))
- {
- descriptor = texturePool.GetDescriptor(textureId);
- }
- else
- {
- // If the ID is not valid, we just return a default descriptor with the most common state.
- // Since this is used for shader specialization, doing so might avoid the need for recompilations.
- descriptor = new TextureDescriptor();
- descriptor.Word4 |= (uint)TextureTarget.Texture2D << 23;
- descriptor.Word5 |= 1u << 31; // Coords normalized.
- }
-
- return descriptor;
- }
-
- /// <summary>
- /// Reads a packed texture and sampler ID (basically, the real texture handle)
- /// from the texture constant buffer.
- /// </summary>
- /// <param name="stageIndex">The number of the shader stage where the texture is bound</param>
- /// <param name="wordOffset">A word offset of the handle on the buffer (the "fake" shader handle)</param>
- /// <param name="textureBufferIndex">Index of the constant buffer holding the texture handles</param>
- /// <param name="samplerBufferIndex">Index of the constant buffer holding the sampler handles</param>
- /// <returns>The packed texture and sampler ID (the real texture handle)</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex)
- {
- (int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
-
- ulong textureBufferAddress = _isCompute
- ? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
- : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
-
- int handle = textureBufferAddress != 0
- ? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
- : 0;
-
- // The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
- // is a 13-bit value. However, in order to also support separate samplers and textures (which uses
- // bindless textures on the shader), we extend it with another value on the higher 16 bits with
- // another offset for the sampler.
- // The shader translator has code to detect separate texture and sampler uses with a bindless texture,
- // turn that into a regular texture access and produce those special handles with values on the higher 16 bits.
- if (handleType != TextureHandleType.CombinedSampler)
- {
- int samplerHandle;
-
- if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
- {
- ulong samplerBufferAddress = _isCompute
- ? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
- : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
-
- samplerHandle = samplerBufferAddress != 0
- ? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
- : 0;
- }
- else
- {
- samplerHandle = samplerWordOffset;
- }
-
- if (handleType == TextureHandleType.SeparateSamplerId ||
- handleType == TextureHandleType.SeparateConstantSamplerHandle)
- {
- samplerHandle <<= 20;
- }
-
- handle |= samplerHandle;
- }
-
- return handle;
- }
-
- /// <summary>
- /// Gets the texture and sampler pool for the GPU virtual address that are currently set.
- /// </summary>
- /// <returns>The texture and sampler pools</returns>
- private (TexturePool, SamplerPool) GetPools()
- {
- MemoryManager memoryManager = _channel.MemoryManager;
-
- TexturePool texturePool = _texturePool;
- SamplerPool samplerPool = _samplerPool;
-
- if (texturePool == null)
- {
- ulong poolAddress = memoryManager.Translate(_texturePoolGpuVa);
-
- if (poolAddress != MemoryManager.PteUnmapped)
- {
- texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId);
- _texturePool = texturePool;
- }
- }
-
- if (samplerPool == null)
- {
- ulong poolAddress = memoryManager.Translate(_samplerPoolGpuVa);
-
- if (poolAddress != MemoryManager.PteUnmapped)
- {
- samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId);
- _samplerPool = samplerPool;
- }
- }
-
- return (texturePool, samplerPool);
- }
-
- /// <summary>
- /// Forces the texture and sampler pools to be re-loaded from the cache on next use.
- /// </summary>
- /// <remarks>
- /// This should be called if the memory mappings change, to ensure the correct pools are being used.
- /// </remarks>
- public void ReloadPools()
- {
- _samplerPool = null;
- _texturePool = null;
- }
-
- /// <summary>
- /// Force all bound textures and images to be rebound the next time CommitBindings is called.
- /// </summary>
- public void Rebind()
- {
- Array.Clear(_textureState);
- Array.Clear(_imageState);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
deleted file mode 100644
index c3243cf2..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ /dev/null
@@ -1,1180 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Engine.Threed;
-using Ryujinx.Graphics.Gpu.Engine.Twod;
-using Ryujinx.Graphics.Gpu.Engine.Types;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Texture;
-using Ryujinx.Memory.Range;
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture cache.
- /// </summary>
- class TextureCache : IDisposable
- {
- private readonly struct OverlapInfo
- {
- public TextureViewCompatibility Compatibility { get; }
- public int FirstLayer { get; }
- public int FirstLevel { get; }
-
- public OverlapInfo(TextureViewCompatibility compatibility, int firstLayer, int firstLevel)
- {
- Compatibility = compatibility;
- FirstLayer = firstLayer;
- FirstLevel = firstLevel;
- }
- }
-
- private const int OverlapsBufferInitialCapacity = 10;
- private const int OverlapsBufferMaxCapacity = 10000;
-
- private readonly GpuContext _context;
- private readonly PhysicalMemory _physicalMemory;
-
- private readonly MultiRangeList<Texture> _textures;
- private readonly HashSet<Texture> _partiallyMappedTextures;
-
- private Texture[] _textureOverlaps;
- private OverlapInfo[] _overlapInfo;
-
- private readonly AutoDeleteCache _cache;
-
- /// <summary>
- /// Constructs a new instance of the texture manager.
- /// </summary>
- /// <param name="context">The GPU context that the texture manager belongs to</param>
- /// <param name="physicalMemory">Physical memory where the textures managed by this cache are mapped</param>
- public TextureCache(GpuContext context, PhysicalMemory physicalMemory)
- {
- _context = context;
- _physicalMemory = physicalMemory;
-
- _textures = new MultiRangeList<Texture>();
- _partiallyMappedTextures = new HashSet<Texture>();
-
- _textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
- _overlapInfo = new OverlapInfo[OverlapsBufferInitialCapacity];
-
- _cache = new AutoDeleteCache();
- }
-
- /// <summary>
- /// Handles removal of textures written to a memory region being unmapped.
- /// </summary>
- /// <param name="sender">Sender object</param>
- /// <param name="e">Event arguments</param>
- public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
- {
- Texture[] overlaps = new Texture[10];
- int overlapCount;
-
- MultiRange unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
-
- lock (_textures)
- {
- overlapCount = _textures.FindOverlaps(unmapped, ref overlaps);
- }
-
- for (int i = 0; i < overlapCount; i++)
- {
- overlaps[i].Unmapped(unmapped);
- }
-
- // If any range was previously unmapped, we also need to purge
- // all partially mapped texture, as they might be fully mapped now.
- for (int i = 0; i < unmapped.Count; i++)
- {
- if (unmapped.GetSubRange(i).Address == MemoryManager.PteUnmapped)
- {
- lock (_partiallyMappedTextures)
- {
- foreach (var texture in _partiallyMappedTextures)
- {
- texture.Unmapped(unmapped);
- }
- }
-
- break;
- }
- }
- }
-
- /// <summary>
- /// Determines if a given texture is eligible for upscaling from its info.
- /// </summary>
- /// <param name="info">The texture info to check</param>
- /// <param name="withUpscale">True if the user of the texture would prefer it to be upscaled immediately</param>
- /// <returns>True if eligible</returns>
- private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale)
- {
- if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray || info.Target == Target.Texture2DMultisample) && !info.FormatInfo.IsCompressed)
- {
- return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired;
- }
-
- return TextureScaleMode.Blacklisted;
- }
-
- /// <summary>
- /// Determines if a given texture is "safe" for upscaling from its info.
- /// Note that this is different from being compatible - this elilinates targets that would have detrimental effects when scaled.
- /// </summary>
- /// <param name="info">The texture info to check</param>
- /// <returns>True if safe</returns>
- private static bool UpscaleSafeMode(TextureInfo info)
- {
- // While upscaling works for all targets defined by IsUpscaleCompatible, we additionally blacklist targets here that
- // may have undesirable results (upscaling blur textures) or simply waste GPU resources (upscaling texture atlas).
-
- if (info.Levels > 3)
- {
- // Textures with more than 3 levels are likely to be game textures, rather than render textures.
- // Small textures with full mips are likely to be removed by the next check.
- return false;
- }
-
- if (info.Width < 8 || info.Height < 8)
- {
- // Discount textures with small dimensions.
- return false;
- }
-
- int widthAlignment = (info.IsLinear ? Constants.StrideAlignment : Constants.GobAlignment) / info.FormatInfo.BytesPerPixel;
-
- if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Components == 1))
- {
- // Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas)
- // Detect if the texture is possibly square. Widths may be aligned, so to remove the uncertainty we align both the width and height.
-
- bool possiblySquare = BitUtils.AlignUp(info.Width, widthAlignment) == BitUtils.AlignUp(info.Height, widthAlignment);
-
- if (possiblySquare)
- {
- return false;
- }
- }
-
- if (info.Height < 360)
- {
- int aspectWidth = (int)MathF.Ceiling((info.Height / 9f) * 16f);
- int aspectMaxWidth = BitUtils.AlignUp(aspectWidth, widthAlignment);
- int aspectMinWidth = BitUtils.AlignDown(aspectWidth, widthAlignment);
-
- if (info.Width >= aspectMinWidth && info.Width <= aspectMaxWidth && info.Height < 360)
- {
- // Targets that are roughly 16:9 can only be rescaled if they're equal to or above 360p. (excludes blur and bloom textures)
- return false;
- }
- }
-
- if (info.Width == info.Height * info.Height)
- {
- // Possibly used for a "3D texture" drawn onto a 2D surface.
- // Some games do this to generate a tone mapping LUT without rendering into 3D texture slices.
-
- return false;
- }
-
- return true;
- }
-
- /// <summary>
- /// Lifts the texture to the top of the AutoDeleteCache. This is primarily used to enforce that
- /// data written to a target will be flushed to memory should the texture be deleted, but also
- /// keeps rendered textures alive without a pool reference.
- /// </summary>
- /// <param name="texture">Texture to lift</param>
- public void Lift(Texture texture)
- {
- _cache.Lift(texture);
- }
-
- /// <summary>
- /// Attempts to update a texture's physical memory range.
- /// Returns false if there is an existing texture that matches with the updated range.
- /// </summary>
- /// <param name="texture">Texture to update</param>
- /// <param name="range">New physical memory range</param>
- /// <returns>True if the mapping was updated, false otherwise</returns>
- public bool UpdateMapping(Texture texture, MultiRange range)
- {
- // There cannot be an existing texture compatible with this mapping in the texture cache already.
- int overlapCount = _textures.FindOverlaps(range, ref _textureOverlaps);
-
- for (int i = 0; i < overlapCount; i++)
- {
- var other = _textureOverlaps[i];
-
- if (texture != other &&
- (texture.IsViewCompatible(other.Info, other.Range, true, other.LayerSize, _context.Capabilities, out _, out _) != TextureViewCompatibility.Incompatible ||
- other.IsViewCompatible(texture.Info, texture.Range, true, texture.LayerSize, _context.Capabilities, out _, out _) != TextureViewCompatibility.Incompatible))
- {
- return false;
- }
- }
-
- _textures.Remove(texture);
-
- texture.ReplaceRange(range);
-
- _textures.Add(texture);
-
- return true;
- }
-
- /// <summary>
- /// Tries to find an existing texture, or create a new one if not found.
- /// </summary>
- /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
- /// <param name="copyTexture">Copy texture to find or create</param>
- /// <param name="offset">Offset to be added to the physical texture address</param>
- /// <param name="formatInfo">Format information of the copy texture</param>
- /// <param name="preferScaling">Indicates if the texture should be scaled from the start</param>
- /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
- /// <returns>The texture</returns>
- public Texture FindOrCreateTexture(
- MemoryManager memoryManager,
- TwodTexture copyTexture,
- ulong offset,
- FormatInfo formatInfo,
- bool shouldCreate,
- bool preferScaling,
- Size sizeHint)
- {
- int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
- int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
-
- int width;
-
- if (copyTexture.LinearLayout)
- {
- width = copyTexture.Stride / formatInfo.BytesPerPixel;
- }
- else
- {
- width = copyTexture.Width;
- }
-
- TextureInfo info = new TextureInfo(
- copyTexture.Address.Pack() + offset,
- GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, copyTexture.LinearLayout),
- copyTexture.Height,
- copyTexture.Depth,
- 1,
- 1,
- 1,
- copyTexture.Stride,
- copyTexture.LinearLayout,
- gobBlocksInY,
- gobBlocksInZ,
- 1,
- Target.Texture2D,
- formatInfo);
-
- TextureSearchFlags flags = TextureSearchFlags.ForCopy;
-
- if (preferScaling)
- {
- flags |= TextureSearchFlags.WithUpscale;
- }
-
- if (!shouldCreate)
- {
- flags |= TextureSearchFlags.NoCreate;
- }
-
- Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0);
-
- texture?.SynchronizeMemory();
-
- return texture;
- }
-
- /// <summary>
- /// Tries to find an existing texture, or create a new one if not found.
- /// </summary>
- /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
- /// <param name="colorState">Color buffer texture to find or create</param>
- /// <param name="layered">Indicates if the texture might be accessed with a non-zero layer index</param>
- /// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
- /// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
- /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
- /// <returns>The texture</returns>
- public Texture FindOrCreateTexture(
- MemoryManager memoryManager,
- RtColorState colorState,
- bool layered,
- int samplesInX,
- int samplesInY,
- Size sizeHint)
- {
- 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 && layered
- ? Target.Texture2DMultisampleArray
- : Target.Texture2DMultisample;
- }
- else
- {
- target = colorState.Depth > 1 && layered
- ? 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(
- colorState.Address.Pack(),
- GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, isLinear),
- colorState.Height,
- colorState.Depth,
- 1,
- samplesInX,
- samplesInY,
- stride,
- isLinear,
- gobBlocksInY,
- gobBlocksInZ,
- 1,
- target,
- formatInfo);
-
- int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
-
- Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize);
-
- texture?.SynchronizeMemory();
-
- return texture;
- }
-
- /// <summary>
- /// Tries to find an existing texture, or create a new one if not found.
- /// </summary>
- /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
- /// <param name="dsState">Depth-stencil buffer texture to find or create</param>
- /// <param name="size">Size of the depth-stencil texture</param>
- /// <param name="layered">Indicates if the texture might be accessed with a non-zero layer index</param>
- /// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
- /// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
- /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
- /// <returns>The texture</returns>
- public Texture FindOrCreateTexture(
- MemoryManager memoryManager,
- RtDepthStencilState dsState,
- Size3D size,
- bool layered,
- int samplesInX,
- int samplesInY,
- Size sizeHint)
- {
- int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
- int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
-
- Target target;
-
- if (dsState.MemoryLayout.UnpackIsTarget3D())
- {
- target = Target.Texture3D;
- }
- else if ((samplesInX | samplesInY) != 1)
- {
- target = size.Depth > 1 && layered
- ? Target.Texture2DMultisampleArray
- : Target.Texture2DMultisample;
- }
- else
- {
- target = size.Depth > 1 && layered
- ? Target.Texture2DArray
- : Target.Texture2D;
- }
-
- FormatInfo formatInfo = dsState.Format.Convert();
-
- TextureInfo info = new TextureInfo(
- dsState.Address.Pack(),
- GetMinimumWidthInGob(size.Width, sizeHint.Width, formatInfo.BytesPerPixel, false),
- size.Height,
- size.Depth,
- 1,
- samplesInX,
- samplesInY,
- 0,
- false,
- gobBlocksInY,
- gobBlocksInZ,
- 1,
- target,
- formatInfo);
-
- Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4);
-
- texture?.SynchronizeMemory();
-
- return texture;
- }
-
- /// <summary>
- /// For block linear textures, gets the minimum width of the texture
- /// that would still have the same number of GOBs per row as the original width.
- /// </summary>
- /// <param name="width">The possibly aligned texture width</param>
- /// <param name="minimumWidth">The minimum width that the texture may have without losing data</param>
- /// <param name="bytesPerPixel">Bytes per pixel of the texture format</param>
- /// <param name="isLinear">True if the texture is linear, false for block linear</param>
- /// <returns>The minimum width of the texture with the same amount of GOBs per row</returns>
- private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPerPixel, bool isLinear)
- {
- if (isLinear || (uint)minimumWidth >= (uint)width)
- {
- return width;
- }
-
- // Calculate the minimum possible that would not cause data loss
- // and would be still within the same GOB (aligned size would be the same).
- // This is useful for render and copy operations, where we don't know the
- // exact width of the texture, but it doesn't matter, as long the texture is
- // at least as large as the region being rendered or copied.
-
- int alignment = 64 / bytesPerPixel;
- int widthAligned = BitUtils.AlignUp(width, alignment);
-
- return Math.Clamp(widthAligned - alignment + 1, minimumWidth, widthAligned);
- }
-
- /// <summary>
- /// Tries to find an existing texture, or create a new one if not found.
- /// </summary>
- /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
- /// <param name="flags">The texture search flags, defines texture comparison rules</param>
- /// <param name="info">Texture information of the texture to be found or created</param>
- /// <param name="layerSize">Size in bytes of a single texture layer</param>
- /// <param name="range">Optional ranges of physical memory where the texture data is located</param>
- /// <returns>The texture</returns>
- public Texture FindOrCreateTexture(
- MemoryManager memoryManager,
- TextureSearchFlags flags,
- TextureInfo info,
- int layerSize = 0,
- MultiRange? range = null)
- {
- bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
-
- TextureScaleMode scaleMode = IsUpscaleCompatible(info, (flags & TextureSearchFlags.WithUpscale) != 0);
-
- ulong address;
-
- if (range != null)
- {
- address = range.Value.GetSubRange(0).Address;
- }
- else
- {
- address = memoryManager.Translate(info.GpuAddress);
-
- // If the start address is unmapped, let's try to find a page of memory that is mapped.
- if (address == MemoryManager.PteUnmapped)
- {
- // Make sure that the dimensions are valid before calculating the texture size.
- if (info.Width < 1 || info.Height < 1 || info.Levels < 1)
- {
- return null;
- }
-
- if ((info.Target == Target.Texture3D ||
- info.Target == Target.Texture2DArray ||
- info.Target == Target.Texture2DMultisampleArray ||
- info.Target == Target.CubemapArray) && info.DepthOrLayers < 1)
- {
- return null;
- }
-
- ulong dataSize = (ulong)info.CalculateSizeInfo(layerSize).TotalSize;
-
- address = memoryManager.TranslateFirstMapped(info.GpuAddress, dataSize);
- }
-
- // If address is still invalid, the texture is fully unmapped, so it has no data, just return null.
- if (address == MemoryManager.PteUnmapped)
- {
- return null;
- }
- }
-
- int sameAddressOverlapsCount;
-
- lock (_textures)
- {
- // Try to find a perfect texture match, with the same address and parameters.
- sameAddressOverlapsCount = _textures.FindOverlaps(address, ref _textureOverlaps);
- }
-
- Texture texture = null;
-
- TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch;
-
- for (int index = 0; index < sameAddressOverlapsCount; index++)
- {
- Texture overlap = _textureOverlaps[index];
-
- TextureMatchQuality matchQuality = overlap.IsExactMatch(info, flags);
-
- if (matchQuality != TextureMatchQuality.NoMatch)
- {
- // If the parameters match, we need to make sure the texture is mapped to the same memory regions.
- if (range != null)
- {
- // If a range of memory was supplied, just check if the ranges match.
- if (!overlap.Range.Equals(range.Value))
- {
- continue;
- }
- }
- else
- {
- // If no range was supplied, we can check if the GPU virtual address match. If they do,
- // we know the textures are located at the same memory region.
- // If they don't, it may still be mapped to the same physical region, so we
- // do a more expensive check to tell if they are mapped into the same physical regions.
- // If the GPU VA for the texture has ever been unmapped, then the range must be checked regardless.
- if ((overlap.Info.GpuAddress != info.GpuAddress || overlap.ChangedMapping) &&
- !memoryManager.CompareRange(overlap.Range, info.GpuAddress))
- {
- continue;
- }
- }
- }
-
- if (matchQuality == TextureMatchQuality.Perfect)
- {
- texture = overlap;
- break;
- }
- else if (matchQuality > bestQuality)
- {
- texture = overlap;
- bestQuality = matchQuality;
- }
- }
-
- if (texture != null)
- {
- texture.SynchronizeMemory();
-
- return texture;
- }
- else if (flags.HasFlag(TextureSearchFlags.NoCreate))
- {
- return null;
- }
-
- // Calculate texture sizes, used to find all overlapping textures.
- SizeInfo sizeInfo = info.CalculateSizeInfo(layerSize);
-
- ulong size = (ulong)sizeInfo.TotalSize;
- bool partiallyMapped = false;
-
- if (range == null)
- {
- range = memoryManager.GetPhysicalRegions(info.GpuAddress, size);
-
- for (int i = 0; i < range.Value.Count; i++)
- {
- if (range.Value.GetSubRange(i).Address == MemoryManager.PteUnmapped)
- {
- partiallyMapped = true;
- break;
- }
- }
- }
-
- // Find view compatible matches.
- int overlapsCount;
-
- lock (_textures)
- {
- overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
- }
-
- if (_overlapInfo.Length != _textureOverlaps.Length)
- {
- Array.Resize(ref _overlapInfo, _textureOverlaps.Length);
- }
-
- // =============== Find Texture View of Existing Texture ===============
-
- int fullyCompatible = 0;
-
- // Evaluate compatibility of overlaps, add temporary references
-
- for (int index = 0; index < overlapsCount; index++)
- {
- Texture overlap = _textureOverlaps[index];
- TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
- info,
- range.Value,
- isSamplerTexture,
- sizeInfo.LayerSize,
- _context.Capabilities,
- out int firstLayer,
- out int firstLevel);
-
- if (overlapCompatibility == TextureViewCompatibility.Full)
- {
- if (overlap.IsView)
- {
- overlapCompatibility = TextureViewCompatibility.CopyOnly;
- }
- else
- {
- fullyCompatible++;
- }
- }
-
- _overlapInfo[index] = new OverlapInfo(overlapCompatibility, firstLayer, firstLevel);
- overlap.IncrementReferenceCount();
- }
-
- // Search through the overlaps to find a compatible view and establish any copy dependencies.
-
- for (int index = 0; index < overlapsCount; index++)
- {
- Texture overlap = _textureOverlaps[index];
- OverlapInfo oInfo = _overlapInfo[index];
-
- if (oInfo.Compatibility == TextureViewCompatibility.Full)
- {
- if (!isSamplerTexture)
- {
- // If this is not a sampler texture, the size might be different from the requested size,
- // so we need to make sure the texture information has the correct size for this base texture,
- // before creating the view.
- info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel);
- }
-
- texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
- texture.SynchronizeMemory();
- break;
- }
- else if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0)
- {
- // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
-
- texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
-
- texture.InitializeGroup(true, true, new List<TextureIncompatibleOverlap>());
- texture.InitializeData(false, false);
-
- overlap.SynchronizeMemory();
- overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
- break;
- }
- }
-
- if (texture != null)
- {
- // This texture could be a view of multiple parent textures with different storages, even if it is a view.
- // When a texture is created, make sure all possible dependencies to other textures are created as copies.
- // (even if it could be fulfilled without a copy)
-
- for (int index = 0; index < overlapsCount; index++)
- {
- Texture overlap = _textureOverlaps[index];
- OverlapInfo oInfo = _overlapInfo[index];
-
- if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible)
- {
- if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility))
- {
- texture.Group.RegisterIncompatibleOverlap(new TextureIncompatibleOverlap(overlap.Group, oInfo.Compatibility), true);
- }
- }
- else if (overlap.Group != texture.Group)
- {
- overlap.SynchronizeMemory();
- overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
- }
- }
-
- texture.SynchronizeMemory();
- }
-
- // =============== Create a New Texture ===============
-
- // No match, create a new texture.
- if (texture == null)
- {
- texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
-
- // Step 1: Find textures that are view compatible with the new texture.
- // Any textures that are incompatible will contain garbage data, so they should be removed where possible.
-
- int viewCompatible = 0;
- fullyCompatible = 0;
- bool setData = isSamplerTexture || overlapsCount == 0 || flags.HasFlag(TextureSearchFlags.ForCopy);
-
- bool hasLayerViews = false;
- bool hasMipViews = false;
-
- var incompatibleOverlaps = new List<TextureIncompatibleOverlap>();
-
- for (int index = 0; index < overlapsCount; index++)
- {
- Texture overlap = _textureOverlaps[index];
- bool overlapInCache = overlap.CacheNode != null;
-
- TextureViewCompatibility compatibility = texture.IsViewCompatible(
- overlap.Info,
- overlap.Range,
- exactSize: true,
- overlap.LayerSize,
- _context.Capabilities,
- out int firstLayer,
- out int firstLevel);
-
- if (overlap.IsView && compatibility == TextureViewCompatibility.Full)
- {
- compatibility = TextureViewCompatibility.CopyOnly;
- }
-
- if (compatibility > TextureViewCompatibility.LayoutIncompatible)
- {
- _overlapInfo[viewCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
- _textureOverlaps[index] = _textureOverlaps[viewCompatible];
- _textureOverlaps[viewCompatible] = overlap;
-
- if (compatibility == TextureViewCompatibility.Full)
- {
- if (viewCompatible != fullyCompatible)
- {
- // Swap overlaps so that the fully compatible views have priority.
-
- _overlapInfo[viewCompatible] = _overlapInfo[fullyCompatible];
- _textureOverlaps[viewCompatible] = _textureOverlaps[fullyCompatible];
-
- _overlapInfo[fullyCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
- _textureOverlaps[fullyCompatible] = overlap;
- }
-
- fullyCompatible++;
- }
-
- viewCompatible++;
-
- hasLayerViews |= overlap.Info.GetSlices() < texture.Info.GetSlices();
- hasMipViews |= overlap.Info.Levels < texture.Info.Levels;
- }
- else
- {
- bool dataOverlaps = texture.DataOverlaps(overlap, compatibility);
-
- if (!overlap.IsView && dataOverlaps && !incompatibleOverlaps.Exists(incompatible => incompatible.Group == overlap.Group))
- {
- incompatibleOverlaps.Add(new TextureIncompatibleOverlap(overlap.Group, compatibility));
- }
-
- bool removeOverlap;
- bool modified = overlap.CheckModified(false);
-
- if (overlapInCache || !setData)
- {
- if (!dataOverlaps)
- {
- // Allow textures to overlap if their data does not actually overlap.
- // This typically happens when mip level subranges of a layered texture are used. (each texture fills the gaps of the others)
- continue;
- }
-
- // The overlap texture is going to contain garbage data after we draw, or is generally incompatible.
- // The texture group will obtain copy dependencies for any subresources that are compatible between the two textures,
- // but sometimes its data must be flushed regardless.
-
- // If the texture was modified since its last use, then that data is probably meant to go into this texture.
- // If the data has been modified by the CPU, then it also shouldn't be flushed.
-
- bool flush = overlapInCache && !modified && overlap.AlwaysFlushOnOverlap;
-
- setData |= modified || flush;
-
- if (overlapInCache)
- {
- _cache.Remove(overlap, flush);
- }
-
- removeOverlap = modified;
- }
- else
- {
- // If an incompatible overlapping texture has been modified, then it's data is likely destined for this texture,
- // and the overlapped texture will contain garbage. In this case, it should be removed to save memory.
- removeOverlap = modified;
- }
-
- if (removeOverlap && overlap.Info.Target != Target.TextureBuffer)
- {
- overlap.RemoveFromPools(false);
- }
- }
- }
-
- texture.InitializeGroup(hasLayerViews, hasMipViews, incompatibleOverlaps);
-
- // 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.InitializeData(false, setData);
-
- texture.Group.InitializeOverlaps();
-
- for (int index = 0; index < viewCompatible; index++)
- {
- Texture overlap = _textureOverlaps[index];
-
- OverlapInfo oInfo = _overlapInfo[index];
-
- if (overlap.Group == texture.Group)
- {
- // If the texture group is equal, then this texture (or its parent) is already a view.
- continue;
- }
-
- // Note: If we allow different sizes for those overlaps,
- // we need to make sure that the "info" has the correct size for the parent texture here.
- // Since this is not allowed right now, we don't need to do it.
-
- TextureInfo overlapInfo = overlap.Info;
-
- if (texture.ScaleFactor != overlap.ScaleFactor)
- {
- // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself.
- // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy.
-
- texture.PropagateScale(overlap);
- }
-
- if (oInfo.Compatibility != TextureViewCompatibility.Full)
- {
- // Copy only compatibility, or target texture is already a view.
-
- overlap.SynchronizeMemory();
- texture.CreateCopyDependency(overlap, oInfo.FirstLayer, oInfo.FirstLevel, false);
- }
- else
- {
- TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities, overlap.ScaleFactor);
-
- ITexture newView = texture.HostTexture.CreateView(createInfo, oInfo.FirstLayer, oInfo.FirstLevel);
-
- overlap.SynchronizeMemory();
-
- overlap.HostTexture.CopyTo(newView, 0, 0);
-
- overlap.ReplaceView(texture, overlapInfo, newView, oInfo.FirstLayer, oInfo.FirstLevel);
- }
- }
-
- texture.SynchronizeMemory();
- }
-
- // Sampler textures are managed by the texture pool, all other textures
- // are managed by the auto delete cache.
- if (!isSamplerTexture)
- {
- _cache.Add(texture);
- }
-
- lock (_textures)
- {
- _textures.Add(texture);
- }
-
- if (partiallyMapped)
- {
- lock (_partiallyMappedTextures)
- {
- _partiallyMappedTextures.Add(texture);
- }
- }
-
- ShrinkOverlapsBufferIfNeeded();
-
- for (int i = 0; i < overlapsCount; i++)
- {
- _textureOverlaps[i].DecrementReferenceCount();
- }
-
- return texture;
- }
-
- /// <summary>
- /// Attempt to find a texture on the short duration cache.
- /// </summary>
- /// <param name="descriptor">The texture descriptor</param>
- /// <returns>The texture if found, null otherwise</returns>
- public Texture FindShortCache(in TextureDescriptor descriptor)
- {
- return _cache.FindShortCache(descriptor);
- }
-
- /// <summary>
- /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
- /// </summary>
- /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
- /// <param name="gpuVa">GPU virtual address of the texture</param>
- /// <param name="bpp">Bytes per pixel</param>
- /// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param>
- /// <param name="height">If <paramref name="linear"/> is false, should have the texture height, otherwise ignored</param>
- /// <param name="xCount">Number of pixels to be copied per line</param>
- /// <param name="yCount">Number of lines to be copied</param>
- /// <param name="linear">True if the texture has a linear layout, false otherwise</param>
- /// <param name="gobBlocksInY">If <paramref name="linear"/> is false, the amount of GOB blocks in the Y axis</param>
- /// <param name="gobBlocksInZ">If <paramref name="linear"/> is false, the amount of GOB blocks in the Z axis</param>
- /// <returns>A matching texture, or null if there is no match</returns>
- public Texture FindTexture(
- MemoryManager memoryManager,
- ulong gpuVa,
- int bpp,
- int stride,
- int height,
- int xCount,
- int yCount,
- bool linear,
- int gobBlocksInY,
- int gobBlocksInZ)
- {
- ulong address = memoryManager.Translate(gpuVa);
-
- if (address == MemoryManager.PteUnmapped)
- {
- return null;
- }
-
- int addressMatches = _textures.FindOverlaps(address, ref _textureOverlaps);
- Texture textureMatch = null;
-
- for (int i = 0; i < addressMatches; i++)
- {
- Texture texture = _textureOverlaps[i];
- FormatInfo format = texture.Info.FormatInfo;
-
- if (texture.Info.DepthOrLayers > 1 || texture.Info.Levels > 1 || texture.Info.FormatInfo.IsCompressed)
- {
- // Don't support direct buffer copies to anything that isn't a single 2D image, uncompressed.
- continue;
- }
-
- bool match;
-
- if (linear)
- {
- // Size is not available for linear textures. Use the stride and end of the copy region instead.
-
- match = texture.Info.IsLinear && texture.Info.Stride == stride && yCount == texture.Info.Height;
- }
- else
- {
- // Bpp may be a mismatch between the target texture and the param.
- // Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison.
- // Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size.
-
- bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && height == texture.Info.Height;
- bool formatMatch = !texture.Info.IsLinear &&
- texture.Info.GobBlocksInY == gobBlocksInY &&
- texture.Info.GobBlocksInZ == gobBlocksInZ;
-
- match = sizeMatch && formatMatch;
- }
-
- if (match)
- {
- if (textureMatch == null)
- {
- textureMatch = texture;
- }
- else if (texture.Group != textureMatch.Group)
- {
- return null; // It's ambiguous which texture should match between multiple choices, so leave it up to the slow path.
- }
- }
- }
-
- return textureMatch;
- }
-
- /// <summary>
- /// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
- /// </summary>
- private void ShrinkOverlapsBufferIfNeeded()
- {
- if (_textureOverlaps.Length > OverlapsBufferMaxCapacity)
- {
- Array.Resize(ref _textureOverlaps, OverlapsBufferMaxCapacity);
- }
- }
-
- /// <summary>
- /// Gets a texture creation information from texture information.
- /// This can be used to create new host textures.
- /// </summary>
- /// <param name="info">Texture information</param>
- /// <param name="caps">GPU capabilities</param>
- /// <param name="scale">Texture scale factor, to be applied to the texture size</param>
- /// <returns>The texture creation information</returns>
- public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps, float scale)
- {
- FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(info, caps);
-
- if (info.Target == Target.TextureBuffer && !caps.SupportsSnormBufferTextureFormat)
- {
- // If the host does not support signed normalized formats, we use a signed integer format instead.
- // The shader will need the appropriate conversion code to compensate.
- switch (formatInfo.Format)
- {
- case Format.R8Snorm:
- formatInfo = new FormatInfo(Format.R8Sint, 1, 1, 1, 1);
- break;
- case Format.R16Snorm:
- formatInfo = new FormatInfo(Format.R16Sint, 1, 1, 2, 1);
- break;
- case Format.R8G8Snorm:
- formatInfo = new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2);
- break;
- case Format.R16G16Snorm:
- formatInfo = new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2);
- break;
- case Format.R8G8B8A8Snorm:
- formatInfo = new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4);
- break;
- case Format.R16G16B16A16Snorm:
- formatInfo = new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4);
- break;
- }
- }
-
- int width = info.Width / info.SamplesInX;
- int height = info.Height / info.SamplesInY;
-
- int depth = info.GetDepth() * info.GetLayers();
-
- if (scale != 1f)
- {
- width = (int)MathF.Ceiling(width * scale);
- height = (int)MathF.Ceiling(height * scale);
- }
-
- 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);
- }
-
- /// <summary>
- /// Removes a texture from the cache.
- /// </summary>
- /// <remarks>
- /// This only removes the texture from the internal list, not from the auto-deletion cache.
- /// It may still have live references after the removal.
- /// </remarks>
- /// <param name="texture">The texture to be removed</param>
- public void RemoveTextureFromCache(Texture texture)
- {
- lock (_textures)
- {
- _textures.Remove(texture);
- }
-
- lock (_partiallyMappedTextures)
- {
- _partiallyMappedTextures.Remove(texture);
- }
- }
-
- /// <summary>
- /// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
- /// </summary>
- /// <param name="texture">Texture to add to the short cache</param>
- /// <param name="descriptor">Last used texture descriptor</param>
- public void AddShortCache(Texture texture, ref TextureDescriptor descriptor)
- {
- _cache.AddShortCache(texture, ref descriptor);
- }
-
- /// <summary>
- /// Removes a texture from the short duration cache.
- /// </summary>
- /// <param name="texture">Texture to remove from the short cache</param>
- public void RemoveShortCache(Texture texture)
- {
- _cache.RemoveShortCache(texture);
- }
-
- /// <summary>
- /// Ticks periodic elements of the texture cache.
- /// </summary>
- public void Tick()
- {
- _cache.ProcessShortCache();
- }
-
- /// <summary>
- /// Disposes all textures and samplers in the cache.
- /// It's an error to use the texture cache after disposal.
- /// </summary>
- public void Dispose()
- {
- lock (_textures)
- {
- foreach (Texture texture in _textures)
- {
- texture.Dispose();
- }
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
deleted file mode 100644
index e93ea0c0..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
+++ /dev/null
@@ -1,911 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Texture;
-using System;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture format compatibility checks.
- /// </summary>
- static class TextureCompatibility
- {
- private enum FormatClass
- {
- Unclassified,
- Bc1Rgba,
- Bc2,
- Bc3,
- Bc4,
- Bc5,
- Bc6,
- Bc7,
- Etc2Rgb,
- Etc2Rgba,
- Astc4x4,
- Astc5x4,
- Astc5x5,
- Astc6x5,
- Astc6x6,
- Astc8x5,
- Astc8x6,
- Astc8x8,
- Astc10x5,
- Astc10x6,
- Astc10x8,
- Astc10x10,
- Astc12x10,
- Astc12x12
- }
-
- /// <summary>
- /// Checks if a format is host incompatible.
- /// </summary>
- /// <remarks>
- /// Host incompatible formats can't be used directly, the texture data needs to be converted
- /// to a compatible format first.
- /// </remarks>
- /// <param name="info">Texture information</param>
- /// <param name="caps">Host GPU capabilities</param>
- /// <returns>True if the format is incompatible, false otherwise</returns>
- public static bool IsFormatHostIncompatible(TextureInfo info, Capabilities caps)
- {
- Format originalFormat = info.FormatInfo.Format;
- return ToHostCompatibleFormat(info, caps).Format != originalFormat;
- }
-
- /// <summary>
- /// Converts a incompatible format to a host compatible format, or return the format directly
- /// if it is already host compatible.
- /// </summary>
- /// <remarks>
- /// This can be used to convert a incompatible compressed format to the decompressor
- /// output format.
- /// </remarks>
- /// <param name="info">Texture information</param>
- /// <param name="caps">Host GPU capabilities</param>
- /// <returns>A host compatible format</returns>
- public static FormatInfo ToHostCompatibleFormat(TextureInfo info, Capabilities caps)
- {
- // The host API does not support those compressed formats.
- // We assume software decompression will be done for those textures,
- // and so we adjust the format here to match the decompressor output.
-
- if (!caps.SupportsAstcCompression)
- {
- if (info.FormatInfo.Format.IsAstcUnorm())
- {
- return GraphicsConfig.EnableTextureRecompression
- ? new FormatInfo(Format.Bc7Unorm, 4, 4, 16, 4)
- : new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
- }
- else if (info.FormatInfo.Format.IsAstcSrgb())
- {
- return GraphicsConfig.EnableTextureRecompression
- ? new FormatInfo(Format.Bc7Srgb, 4, 4, 16, 4)
- : new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
- }
- }
-
- if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps))
- {
- switch (info.FormatInfo.Format)
- {
- case Format.Bc1RgbaSrgb:
- case Format.Bc2Srgb:
- case Format.Bc3Srgb:
- case Format.Bc7Srgb:
- return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
- case Format.Bc1RgbaUnorm:
- case Format.Bc2Unorm:
- case Format.Bc3Unorm:
- case Format.Bc7Unorm:
- return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
- case Format.Bc4Unorm:
- return new FormatInfo(Format.R8Unorm, 1, 1, 1, 1);
- case Format.Bc4Snorm:
- return new FormatInfo(Format.R8Snorm, 1, 1, 1, 1);
- case Format.Bc5Unorm:
- return new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2);
- case Format.Bc5Snorm:
- return new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2);
- case Format.Bc6HSfloat:
- case Format.Bc6HUfloat:
- return new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4);
- }
- }
-
- if (!caps.SupportsEtc2Compression)
- {
- switch (info.FormatInfo.Format)
- {
- case Format.Etc2RgbaSrgb:
- case Format.Etc2RgbPtaSrgb:
- case Format.Etc2RgbSrgb:
- return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
- case Format.Etc2RgbaUnorm:
- case Format.Etc2RgbPtaUnorm:
- case Format.Etc2RgbUnorm:
- return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
- }
- }
-
- if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
- {
- if (caps.SupportsR4G4B4A4Format)
- {
- return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
- }
- else
- {
- return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
- }
- }
-
- if (info.FormatInfo.Format == Format.R4G4B4A4Unorm)
- {
- if (!caps.SupportsR4G4B4A4Format)
- {
- return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
- }
- }
- else if (!caps.Supports5BitComponentFormat && info.FormatInfo.Format.Is16BitPacked())
- {
- return new FormatInfo(info.FormatInfo.Format.IsBgr() ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4);
- }
-
- return info.FormatInfo;
- }
-
- /// <summary>
- /// Checks if the host API supports a given texture compression format of the BC family.
- /// </summary>
- /// <param name="format">BC format to be checked</param>
- /// <param name="target">Target usage of the texture</param>
- /// <param name="caps">Host GPU Capabilities</param>
- /// <returns>True if the texture host supports the format with the given target usage, false otherwise</returns>
- public static bool HostSupportsBcFormat(Format format, Target target, Capabilities caps)
- {
- bool not3DOr3DCompressionSupported = target != Target.Texture3D || caps.Supports3DTextureCompression;
-
- switch (format)
- {
- case Format.Bc1RgbaSrgb:
- case Format.Bc1RgbaUnorm:
- case Format.Bc2Srgb:
- case Format.Bc2Unorm:
- case Format.Bc3Srgb:
- case Format.Bc3Unorm:
- return caps.SupportsBc123Compression && not3DOr3DCompressionSupported;
- case Format.Bc4Unorm:
- case Format.Bc4Snorm:
- case Format.Bc5Unorm:
- case Format.Bc5Snorm:
- return caps.SupportsBc45Compression && not3DOr3DCompressionSupported;
- case Format.Bc6HSfloat:
- case Format.Bc6HUfloat:
- case Format.Bc7Srgb:
- case Format.Bc7Unorm:
- return caps.SupportsBc67Compression && not3DOr3DCompressionSupported;
- }
-
- return true;
- }
-
- /// <summary>
- /// Determines whether a texture can flush its data back to guest memory.
- /// </summary>
- /// <param name="info">Texture information</param>
- /// <param name="caps">Host GPU Capabilities</param>
- /// <returns>True if the texture can flush, false otherwise</returns>
- public static bool CanTextureFlush(TextureInfo info, Capabilities caps)
- {
- if (IsFormatHostIncompatible(info, caps))
- {
- return false; // Flushing this format is not supported, as it may have been converted to another host format.
- }
-
- if (info.Target == Target.Texture2DMultisample ||
- info.Target == Target.Texture2DMultisampleArray)
- {
- return false; // Flushing multisample textures is not supported, the host does not allow getting their data.
- }
-
- return true;
- }
-
- /// <summary>
- /// Checks if the texture format matches with the specified texture information.
- /// </summary>
- /// <param name="lhs">Texture information to compare</param>
- /// <param name="rhs">Texture information to compare with</param>
- /// <param name="forSampler">Indicates that the texture will be used for shader sampling</param>
- /// <param name="forCopy">Indicates that the texture will be used as copy source or target</param>
- /// <returns>A value indicating how well the formats match</returns>
- public static TextureMatchQuality FormatMatches(TextureInfo lhs, TextureInfo rhs, bool forSampler, bool forCopy)
- {
- // D32F and R32F texture have the same representation internally,
- // however the R32F format is used to sample from depth textures.
- if (lhs.FormatInfo.Format == Format.D32Float && rhs.FormatInfo.Format == Format.R32Float && (forSampler || forCopy))
- {
- return TextureMatchQuality.FormatAlias;
- }
-
- if (forCopy)
- {
- // The 2D engine does not support depth-stencil formats, so it will instead
- // use equivalent color formats. We must also consider them as compatible.
- if (lhs.FormatInfo.Format == Format.S8Uint && rhs.FormatInfo.Format == Format.R8Unorm)
- {
- return TextureMatchQuality.FormatAlias;
- }
-
- if (lhs.FormatInfo.Format == Format.D16Unorm && rhs.FormatInfo.Format == Format.R16Unorm)
- {
- return TextureMatchQuality.FormatAlias;
- }
-
- if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
- lhs.FormatInfo.Format == Format.S8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
- {
- return TextureMatchQuality.FormatAlias;
- }
- }
-
- return lhs.FormatInfo.Format == rhs.FormatInfo.Format ? TextureMatchQuality.Perfect : TextureMatchQuality.NoMatch;
- }
-
- /// <summary>
- /// Checks if the texture layout specified matches with this texture layout.
- /// The layout information is composed of the Stride for linear textures, or GOB block size
- /// for block linear textures.
- /// </summary>
- /// <param name="lhs">Texture information to compare</param>
- /// <param name="rhs">Texture information to compare with</param>
- /// <returns>True if the layout matches, false otherwise</returns>
- public static bool LayoutMatches(TextureInfo lhs, TextureInfo rhs)
- {
- if (lhs.IsLinear != rhs.IsLinear)
- {
- return false;
- }
-
- // For linear textures, gob block sizes are ignored.
- // For block linear textures, the stride is ignored.
- if (rhs.IsLinear)
- {
- return lhs.Stride == rhs.Stride;
- }
- else
- {
- return lhs.GobBlocksInY == rhs.GobBlocksInY &&
- lhs.GobBlocksInZ == rhs.GobBlocksInZ;
- }
- }
-
- /// <summary>
- /// Obtain the minimum compatibility level of two provided view compatibility results.
- /// </summary>
- /// <param name="first">The first compatibility level</param>
- /// <param name="second">The second compatibility level</param>
- /// <returns>The minimum compatibility level of two provided view compatibility results</returns>
- public static TextureViewCompatibility PropagateViewCompatibility(TextureViewCompatibility first, TextureViewCompatibility second)
- {
- if (first == TextureViewCompatibility.Incompatible || second == TextureViewCompatibility.Incompatible)
- {
- return TextureViewCompatibility.Incompatible;
- }
- else if (first == TextureViewCompatibility.LayoutIncompatible || second == TextureViewCompatibility.LayoutIncompatible)
- {
- return TextureViewCompatibility.LayoutIncompatible;
- }
- else if (first == TextureViewCompatibility.CopyOnly || second == TextureViewCompatibility.CopyOnly)
- {
- return TextureViewCompatibility.CopyOnly;
- }
- else
- {
- return TextureViewCompatibility.Full;
- }
- }
-
- /// <summary>
- /// Checks if the sizes of two texture levels are copy compatible.
- /// </summary>
- /// <param name="lhs">Texture information of the texture view</param>
- /// <param name="rhs">Texture information of the texture view to match against</param>
- /// <param name="lhsLevel">Mipmap level of the texture view in relation to this texture</param>
- /// <param name="rhsLevel">Mipmap level of the texture view in relation to the second texture</param>
- /// <returns>True if both levels are view compatible</returns>
- public static bool CopySizeMatches(TextureInfo lhs, TextureInfo rhs, int lhsLevel, int rhsLevel)
- {
- Size size = GetAlignedSize(lhs, lhsLevel);
-
- Size otherSize = GetAlignedSize(rhs, rhsLevel);
-
- if (size.Width == otherSize.Width && size.Height == otherSize.Height)
- {
- return true;
- }
- else if (lhs.IsLinear && rhs.IsLinear)
- {
- // Copy between linear textures with matching stride.
- int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> lhsLevel), Constants.StrideAlignment);
-
- return stride == rhs.Stride;
- }
- else
- {
- return false;
- }
- }
-
- /// <summary>
- /// Checks if the sizes of two given textures are view compatible.
- /// </summary>
- /// <param name="lhs">Texture information of the texture view</param>
- /// <param name="rhs">Texture information of the texture view to match against</param>
- /// <param name="exact">Indicates if the sizes must be exactly equal</param>
- /// <param name="level">Mipmap level of the texture view in relation to this texture</param>
- /// <returns>The view compatibility level of the view sizes</returns>
- public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact, int level)
- {
- Size lhsAlignedSize = GetAlignedSize(lhs, level);
- Size rhsAlignedSize = GetAlignedSize(rhs);
-
- Size lhsSize = GetSizeInBlocks(lhs, level);
- Size rhsSize = GetSizeInBlocks(rhs);
-
- bool alignedWidthMatches = lhsAlignedSize.Width == rhsAlignedSize.Width;
-
- if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo))
- {
- alignedWidthMatches = lhsSize.Width * lhs.FormatInfo.BytesPerPixel == rhsSize.Width * rhs.FormatInfo.BytesPerPixel;
- }
-
- TextureViewCompatibility result = TextureViewCompatibility.Full;
-
- // For copies, we can copy a subset of the 3D texture slices,
- // so the depth may be different in this case.
- if (rhs.Target == Target.Texture3D && lhsSize.Depth != rhsSize.Depth)
- {
- result = TextureViewCompatibility.CopyOnly;
- }
-
- // Some APIs align the width for copy and render target textures,
- // so the width may not match in this case for different uses of the same texture.
- // To account for this, we compare the aligned width here.
- // We expect height to always match exactly, if the texture is the same.
- if (alignedWidthMatches && lhsSize.Height == rhsSize.Height)
- {
- return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width
- ? TextureViewCompatibility.CopyOnly
- : result;
- }
- else if (lhs.IsLinear && rhs.IsLinear && lhsSize.Height == rhsSize.Height)
- {
- // Copy between linear textures with matching stride.
- int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
-
- return stride == rhs.Stride ? TextureViewCompatibility.CopyOnly : TextureViewCompatibility.LayoutIncompatible;
- }
- else
- {
- return TextureViewCompatibility.LayoutIncompatible;
- }
- }
-
- /// <summary>
- /// Checks if the potential child texture fits within the level and layer bounds of the parent.
- /// </summary>
- /// <param name="parent">Texture information for the parent</param>
- /// <param name="child">Texture information for the child</param>
- /// <param name="layer">Base layer of the child texture</param>
- /// <param name="level">Base level of the child texture</param>
- /// <returns>Full compatiblity if the child's layer and level count fit within the parent, incompatible otherwise</returns>
- public static TextureViewCompatibility ViewSubImagesInBounds(TextureInfo parent, TextureInfo child, int layer, int level)
- {
- if (level + child.Levels <= parent.Levels &&
- layer + child.GetSlices() <= parent.GetSlices())
- {
- return TextureViewCompatibility.Full;
- }
- else
- {
- return TextureViewCompatibility.LayoutIncompatible;
- }
- }
-
- /// <summary>
- /// Checks if the texture sizes of the supplied texture informations match.
- /// </summary>
- /// <param name="lhs">Texture information to compare</param>
- /// <param name="rhs">Texture information to compare with</param>
- /// <param name="exact">Indicates if the size must be exactly equal between the textures, or if <paramref name="rhs"/> is allowed to be larger</param>
- /// <returns>True if the sizes matches, false otherwise</returns>
- public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact)
- {
- if (lhs.GetLayers() != rhs.GetLayers())
- {
- return false;
- }
-
- Size lhsSize = GetSizeInBlocks(lhs);
- Size rhsSize = GetSizeInBlocks(rhs);
-
- if (exact || lhs.IsLinear || rhs.IsLinear)
- {
- return lhsSize.Width == rhsSize.Width &&
- lhsSize.Height == rhsSize.Height &&
- lhsSize.Depth == rhsSize.Depth;
- }
- else
- {
- Size lhsAlignedSize = GetAlignedSize(lhs);
- Size rhsAlignedSize = GetAlignedSize(rhs);
-
- return lhsAlignedSize.Width == rhsAlignedSize.Width &&
- lhsSize.Width >= rhsSize.Width &&
- lhsSize.Height == rhsSize.Height &&
- lhsSize.Depth == rhsSize.Depth;
- }
- }
-
- /// <summary>
- /// Gets the aligned sizes for the given dimensions, using the specified texture information.
- /// The alignment depends on the texture layout and format bytes per pixel.
- /// </summary>
- /// <param name="info">Texture information to calculate the aligned size from</param>
- /// <param name="width">The width to be aligned</param>
- /// <param name="height">The height to be aligned</param>
- /// <param name="depth">The depth to be aligned</param>
- /// <returns>The aligned texture size</returns>
- private static Size GetAlignedSize(TextureInfo info, int width, int height, int depth)
- {
- if (info.IsLinear)
- {
- return SizeCalculator.GetLinearAlignedSize(
- width,
- height,
- info.FormatInfo.BlockWidth,
- info.FormatInfo.BlockHeight,
- info.FormatInfo.BytesPerPixel);
- }
- else
- {
- return SizeCalculator.GetBlockLinearAlignedSize(
- width,
- height,
- depth,
- info.FormatInfo.BlockWidth,
- info.FormatInfo.BlockHeight,
- info.FormatInfo.BytesPerPixel,
- info.GobBlocksInY,
- info.GobBlocksInZ,
- info.GobBlocksInTileX);
- }
- }
-
- /// <summary>
- /// Gets the aligned sizes of the specified texture information.
- /// The alignment depends on the texture layout and format bytes per pixel.
- /// </summary>
- /// <param name="info">Texture information to calculate the aligned size from</param>
- /// <param name="level">Mipmap level for texture views</param>
- /// <returns>The aligned texture size</returns>
- public static Size GetAlignedSize(TextureInfo info, int level = 0)
- {
- int width = Math.Max(1, info.Width >> level);
- int height = Math.Max(1, info.Height >> level);
- int depth = Math.Max(1, info.GetDepth() >> level);
-
- return GetAlignedSize(info, width, height, depth);
- }
-
- /// <summary>
- /// Gets the size in blocks for the given texture information.
- /// For non-compressed formats, that's the same as the regular size.
- /// </summary>
- /// <param name="info">Texture information to calculate the aligned size from</param>
- /// <param name="level">Mipmap level for texture views</param>
- /// <returns>The texture size in blocks</returns>
- public static Size GetSizeInBlocks(TextureInfo info, int level = 0)
- {
- int width = Math.Max(1, info.Width >> level);
- int height = Math.Max(1, info.Height >> level);
- int depth = Math.Max(1, info.GetDepth() >> level);
-
- return new Size(
- BitUtils.DivRoundUp(width, info.FormatInfo.BlockWidth),
- BitUtils.DivRoundUp(height, info.FormatInfo.BlockHeight),
- depth);
- }
-
- /// <summary>
- /// Check if it's possible to create a view with the layout of the second texture information from the first.
- /// The layout information is composed of the Stride for linear textures, or GOB block size
- /// for block linear textures.
- /// </summary>
- /// <param name="lhs">Texture information of the texture view</param>
- /// <param name="rhs">Texture information of the texture view to compare against</param>
- /// <param name="level">Start level of the texture view, in relation with the first texture</param>
- /// <returns>True if the layout is compatible, false otherwise</returns>
- public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int level)
- {
- if (lhs.IsLinear != rhs.IsLinear)
- {
- return false;
- }
-
- // For linear textures, gob block sizes are ignored.
- // For block linear textures, the stride is ignored.
- if (rhs.IsLinear)
- {
- int stride = Math.Max(1, lhs.Stride >> level);
- stride = BitUtils.AlignUp(stride, Constants.StrideAlignment);
-
- return stride == rhs.Stride;
- }
- else
- {
- int height = Math.Max(1, lhs.Height >> level);
- int depth = Math.Max(1, lhs.GetDepth() >> level);
-
- (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
- height,
- depth,
- lhs.FormatInfo.BlockHeight,
- lhs.GobBlocksInY,
- lhs.GobBlocksInZ);
-
- return gobBlocksInY == rhs.GobBlocksInY &&
- gobBlocksInZ == rhs.GobBlocksInZ;
- }
- }
-
- /// <summary>
- /// Check if it's possible to create a view with the layout of the second texture information from the first.
- /// The layout information is composed of the Stride for linear textures, or GOB block size
- /// for block linear textures.
- /// </summary>
- /// <param name="lhs">Texture information of the texture view</param>
- /// <param name="rhs">Texture information of the texture view to compare against</param>
- /// <param name="lhsLevel">Start level of the texture view, in relation with the first texture</param>
- /// <param name="rhsLevel">Start level of the texture view, in relation with the second texture</param>
- /// <returns>True if the layout is compatible, false otherwise</returns>
- public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int lhsLevel, int rhsLevel)
- {
- if (lhs.IsLinear != rhs.IsLinear)
- {
- return false;
- }
-
- // For linear textures, gob block sizes are ignored.
- // For block linear textures, the stride is ignored.
- if (rhs.IsLinear)
- {
- int lhsStride = Math.Max(1, lhs.Stride >> lhsLevel);
- lhsStride = BitUtils.AlignUp(lhsStride, Constants.StrideAlignment);
-
- int rhsStride = Math.Max(1, rhs.Stride >> rhsLevel);
- rhsStride = BitUtils.AlignUp(rhsStride, Constants.StrideAlignment);
-
- return lhsStride == rhsStride;
- }
- else
- {
- int lhsHeight = Math.Max(1, lhs.Height >> lhsLevel);
- int lhsDepth = Math.Max(1, lhs.GetDepth() >> lhsLevel);
-
- (int lhsGobBlocksInY, int lhsGobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
- lhsHeight,
- lhsDepth,
- lhs.FormatInfo.BlockHeight,
- lhs.GobBlocksInY,
- lhs.GobBlocksInZ);
-
- int rhsHeight = Math.Max(1, rhs.Height >> rhsLevel);
- int rhsDepth = Math.Max(1, rhs.GetDepth() >> rhsLevel);
-
- (int rhsGobBlocksInY, int rhsGobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
- rhsHeight,
- rhsDepth,
- rhs.FormatInfo.BlockHeight,
- rhs.GobBlocksInY,
- rhs.GobBlocksInZ);
-
- return lhsGobBlocksInY == rhsGobBlocksInY &&
- lhsGobBlocksInZ == rhsGobBlocksInZ;
- }
- }
-
- /// <summary>
- /// Checks if the view format of the first texture format is compatible with the format of the second.
- /// In general, the formats are considered compatible if the bytes per pixel values are equal,
- /// but there are more complex rules for some formats, like compressed or depth-stencil formats.
- /// This follows the host API copy compatibility rules.
- /// </summary>
- /// <param name="lhs">Texture information of the texture view</param>
- /// <param name="rhs">Texture information of the texture view</param>
- /// <param name="caps">Host GPU capabilities</param>
- /// <returns>The view compatibility level of the texture formats</returns>
- public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps)
- {
- FormatInfo lhsFormat = lhs.FormatInfo;
- FormatInfo rhsFormat = rhs.FormatInfo;
-
- if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil())
- {
- return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
- }
-
- if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps))
- {
- return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
- }
-
- if (lhsFormat.IsCompressed && rhsFormat.IsCompressed)
- {
- FormatClass lhsClass = GetFormatClass(lhsFormat.Format);
- FormatClass rhsClass = GetFormatClass(rhsFormat.Format);
-
- return lhsClass == rhsClass ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
- }
- else if (lhsFormat.BytesPerPixel == rhsFormat.BytesPerPixel)
- {
- return lhs.FormatInfo.IsCompressed == rhs.FormatInfo.IsCompressed
- ? TextureViewCompatibility.Full
- : TextureViewCompatibility.CopyOnly;
- }
- else if (IsIncompatibleFormatAliasingAllowed(lhsFormat, rhsFormat))
- {
- return TextureViewCompatibility.CopyOnly;
- }
-
- return TextureViewCompatibility.Incompatible;
- }
-
- /// <summary>
- /// Checks if aliasing of two formats that would normally be considered incompatible be allowed,
- /// using copy dependencies.
- /// </summary>
- /// <param name="lhsFormat">Format information of the first texture</param
- /// <param name="rhsFormat">Format information of the second texture</param>
- /// <returns>True if aliasing should be allowed, false otherwise</returns>
- private static bool IsIncompatibleFormatAliasingAllowed(FormatInfo lhsFormat, FormatInfo rhsFormat)
- {
- // Some games will try to alias textures with incompatible foramts, with different BPP (bytes per pixel).
- // We allow that in some cases as long Width * BPP is equal on both textures.
- // This is very conservative right now as we want to avoid copies as much as possible,
- // so we only consider the formats we have seen being aliased.
-
- if (rhsFormat.BytesPerPixel < lhsFormat.BytesPerPixel)
- {
- (lhsFormat, rhsFormat) = (rhsFormat, lhsFormat);
- }
-
- return lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm;
- }
-
- /// <summary>
- /// Check if the target of the first texture view information is compatible with the target of the second texture view information.
- /// This follows the host API target compatibility rules.
- /// </summary>
- /// <param name="lhs">Texture information of the texture view</param
- /// <param name="rhs">Texture information of the texture view</param>
- /// <param name="caps">Host GPU capabilities</param>
- /// <returns>True if the targets are compatible, false otherwise</returns>
- public static TextureViewCompatibility ViewTargetCompatible(TextureInfo lhs, TextureInfo rhs, ref Capabilities caps)
- {
- bool result = false;
- switch (lhs.Target)
- {
- case Target.Texture1D:
- case Target.Texture1DArray:
- result = rhs.Target == Target.Texture1D ||
- rhs.Target == Target.Texture1DArray;
- break;
-
- case Target.Texture2D:
- result = rhs.Target == Target.Texture2D ||
- rhs.Target == Target.Texture2DArray;
- break;
-
- case Target.Texture2DArray:
- result = rhs.Target == Target.Texture2D ||
- rhs.Target == Target.Texture2DArray;
-
- if (rhs.Target == Target.Cubemap || rhs.Target == Target.CubemapArray)
- {
- return caps.SupportsCubemapView ? TextureViewCompatibility.Full : TextureViewCompatibility.CopyOnly;
- }
- break;
- case Target.Cubemap:
- case Target.CubemapArray:
- result = rhs.Target == Target.Cubemap ||
- rhs.Target == Target.CubemapArray;
-
- if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
- {
- return caps.SupportsCubemapView ? TextureViewCompatibility.Full : TextureViewCompatibility.CopyOnly;
- }
- break;
- case Target.Texture2DMultisample:
- case Target.Texture2DMultisampleArray:
- if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
- {
- return TextureViewCompatibility.CopyOnly;
- }
-
- result = rhs.Target == Target.Texture2DMultisample ||
- rhs.Target == Target.Texture2DMultisampleArray;
- break;
-
- case Target.Texture3D:
- if (rhs.Target == Target.Texture2D)
- {
- return TextureViewCompatibility.CopyOnly;
- }
-
- result = rhs.Target == Target.Texture3D;
- break;
- }
-
- return result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
- }
-
- /// <summary>
- /// Checks if a swizzle component in two textures functionally match, taking into account if the components are defined.
- /// </summary>
- /// <param name="lhs">Texture information to compare</param>
- /// <param name="rhs">Texture information to compare with</param>
- /// <param name="swizzleLhs">Swizzle component for the first texture</param>
- /// <param name="swizzleRhs">Swizzle component for the second texture</param>
- /// <param name="component">Component index, starting at 0 for red</param>
- /// <returns>True if the swizzle components functionally match, false othersize</returns>
- private static bool SwizzleComponentMatches(TextureInfo lhs, TextureInfo rhs, SwizzleComponent swizzleLhs, SwizzleComponent swizzleRhs, int component)
- {
- int lhsComponents = lhs.FormatInfo.Components;
- int rhsComponents = rhs.FormatInfo.Components;
-
- if (lhsComponents == 4 && rhsComponents == 4)
- {
- return swizzleLhs == swizzleRhs;
- }
-
- // Swizzles after the number of components a format defines are "undefined".
- // We allow these to not be equal under certain circumstances.
- // This can only happen when there are less than 4 components in a format.
- // It tends to happen when float depth textures are sampled.
-
- bool lhsDefined = (swizzleLhs - SwizzleComponent.Red) < lhsComponents;
- bool rhsDefined = (swizzleRhs - SwizzleComponent.Red) < rhsComponents;
-
- if (lhsDefined == rhsDefined)
- {
- // If both are undefined, return true. Otherwise just check if they're equal.
- return lhsDefined ? swizzleLhs == swizzleRhs : true;
- }
- else
- {
- SwizzleComponent defined = lhsDefined ? swizzleLhs : swizzleRhs;
- SwizzleComponent undefined = lhsDefined ? swizzleRhs : swizzleLhs;
-
- // Undefined swizzle can be matched by a forced value (0, 1), exact equality, or expected value.
- // For example, R___ matches R001, RGBA but not RBGA.
- return defined == undefined || defined < SwizzleComponent.Red || defined == SwizzleComponent.Red + component;
- }
- }
-
- /// <summary>
- /// Checks if the texture shader sampling parameters of two texture informations match.
- /// </summary>
- /// <param name="lhs">Texture information to compare</param>
- /// <param name="rhs">Texture information to compare with</param>
- /// <returns>True if the texture shader sampling parameters matches, false otherwise</returns>
- public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs)
- {
- return lhs.DepthStencilMode == rhs.DepthStencilMode &&
- SwizzleComponentMatches(lhs, rhs, lhs.SwizzleR, rhs.SwizzleR, 0) &&
- SwizzleComponentMatches(lhs, rhs, lhs.SwizzleG, rhs.SwizzleG, 1) &&
- SwizzleComponentMatches(lhs, rhs, lhs.SwizzleB, rhs.SwizzleB, 2) &&
- SwizzleComponentMatches(lhs, rhs, lhs.SwizzleA, rhs.SwizzleA, 3);
- }
-
- /// <summary>
- /// Check if the texture target and samples count (for multisampled textures) matches.
- /// </summary>
- /// <param name="first">Texture information to compare with</param>
- /// <param name="rhs">Texture information to compare with</param>
- /// <returns>True if the texture target and samples count matches, false otherwise</returns>
- public static bool TargetAndSamplesCompatible(TextureInfo lhs, TextureInfo rhs)
- {
- return lhs.Target == rhs.Target &&
- lhs.SamplesInX == rhs.SamplesInX &&
- lhs.SamplesInY == rhs.SamplesInY;
- }
-
- /// <summary>
- /// Gets the texture format class, for compressed textures, or Unclassified otherwise.
- /// </summary>
- /// <param name="format">The format</param>
- /// <returns>Format class</returns>
- private static FormatClass GetFormatClass(Format format)
- {
- switch (format)
- {
- 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;
- case Format.Etc2RgbSrgb:
- case Format.Etc2RgbUnorm:
- return FormatClass.Etc2Rgb;
- case Format.Etc2RgbaSrgb:
- case Format.Etc2RgbaUnorm:
- return FormatClass.Etc2Rgba;
- case Format.Astc4x4Srgb:
- case Format.Astc4x4Unorm:
- return FormatClass.Astc4x4;
- case Format.Astc5x4Srgb:
- case Format.Astc5x4Unorm:
- return FormatClass.Astc5x4;
- case Format.Astc5x5Srgb:
- case Format.Astc5x5Unorm:
- return FormatClass.Astc5x5;
- case Format.Astc6x5Srgb:
- case Format.Astc6x5Unorm:
- return FormatClass.Astc6x5;
- case Format.Astc6x6Srgb:
- case Format.Astc6x6Unorm:
- return FormatClass.Astc6x6;
- case Format.Astc8x5Srgb:
- case Format.Astc8x5Unorm:
- return FormatClass.Astc8x5;
- case Format.Astc8x6Srgb:
- case Format.Astc8x6Unorm:
- return FormatClass.Astc8x6;
- case Format.Astc8x8Srgb:
- case Format.Astc8x8Unorm:
- return FormatClass.Astc8x8;
- case Format.Astc10x5Srgb:
- case Format.Astc10x5Unorm:
- return FormatClass.Astc10x5;
- case Format.Astc10x6Srgb:
- case Format.Astc10x6Unorm:
- return FormatClass.Astc10x6;
- case Format.Astc10x8Srgb:
- case Format.Astc10x8Unorm:
- return FormatClass.Astc10x8;
- case Format.Astc10x10Srgb:
- case Format.Astc10x10Unorm:
- return FormatClass.Astc10x10;
- case Format.Astc12x10Srgb:
- case Format.Astc12x10Unorm:
- return FormatClass.Astc12x10;
- case Format.Astc12x12Srgb:
- case Format.Astc12x12Unorm:
- return FormatClass.Astc12x12;
- }
-
- return FormatClass.Unclassified;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs b/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
deleted file mode 100644
index 359069bc..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture swizzle color component.
- /// </summary>
- enum TextureComponent
- {
- Zero = 0,
- Red = 2,
- Green = 3,
- Blue = 4,
- Alpha = 5,
- OneSI = 6,
- OneF = 7
- }
-
- static class TextureComponentConverter
- {
- /// <summary>
- /// Converts the texture swizzle color component enum to the respective Graphics Abstraction Layer enum.
- /// </summary>
- /// <param name="component">Texture swizzle color component</param>
- /// <returns>Converted enum</returns>
- 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;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDependency.cs b/Ryujinx.Graphics.Gpu/Image/TextureDependency.cs
deleted file mode 100644
index 269ddbd9..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureDependency.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// One side of a two-way dependency between one texture view and another.
- /// Contains a reference to the handle owning the dependency, and the other dependency.
- /// </summary>
- class TextureDependency
- {
- /// <summary>
- /// The handle that owns this dependency.
- /// </summary>
- public TextureGroupHandle Handle;
-
- /// <summary>
- /// The other dependency linked to this one, which belongs to another handle.
- /// </summary>
- public TextureDependency Other;
-
- /// <summary>
- /// Create a new texture dependency.
- /// </summary>
- /// <param name="handle">The handle that owns the dependency</param>
- public TextureDependency(TextureGroupHandle handle)
- {
- Handle = handle;
- }
-
- /// <summary>
- /// Signal that the owner of this dependency has been modified,
- /// meaning that the other dependency's handle must defer a copy from it.
- /// </summary>
- public void SignalModified()
- {
- Other.Handle.DeferCopy(Handle);
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
deleted file mode 100644
index 3e35f8d2..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
+++ /dev/null
@@ -1,273 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.Intrinsics;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Maxwell texture descriptor, as stored on the GPU texture pool memory region.
- /// </summary>
- struct TextureDescriptor : ITextureDescriptor, IEquatable<TextureDescriptor>
- {
-#pragma warning disable CS0649
- public uint Word0;
- public uint Word1;
- public uint Word2;
- public uint Word3;
- public uint Word4;
- public uint Word5;
- public uint Word6;
- public uint Word7;
-#pragma warning restore CS0649
-
- /// <summary>
- /// Unpacks Maxwell texture format integer.
- /// </summary>
- /// <returns>The texture format integer</returns>
- public uint UnpackFormat()
- {
- return Word0 & 0x8007ffff;
- }
-
- /// <summary>
- /// Unpacks the swizzle component for the texture red color channel.
- /// </summary>
- /// <returns>The swizzle component</returns>
- public TextureComponent UnpackSwizzleR()
- {
- return(TextureComponent)((Word0 >> 19) & 7);
- }
-
- /// <summary>
- /// Unpacks the swizzle component for the texture green color channel.
- /// </summary>
- /// <returns>The swizzle component</returns>
- public TextureComponent UnpackSwizzleG()
- {
- return(TextureComponent)((Word0 >> 22) & 7);
- }
-
- /// <summary>
- /// Unpacks the swizzle component for the texture blue color channel.
- /// </summary>
- /// <returns>The swizzle component</returns>
- public TextureComponent UnpackSwizzleB()
- {
- return(TextureComponent)((Word0 >> 25) & 7);
- }
-
- /// <summary>
- /// Unpacks the swizzle component for the texture alpha color channel.
- /// </summary>
- /// <returns>The swizzle component</returns>
- public TextureComponent UnpackSwizzleA()
- {
- return(TextureComponent)((Word0 >> 28) & 7);
- }
-
- /// <summary>
- /// Unpacks the 40-bits texture GPU virtual address.
- /// </summary>
- /// <returns>The GPU virtual address</returns>
- public ulong UnpackAddress()
- {
- return Word1 | ((ulong)(Word2 & 0xffff) << 32);
- }
-
- /// <summary>
- /// Unpacks texture descriptor type for this texture descriptor.
- /// This defines the texture layout, among other things.
- /// </summary>
- /// <returns>The texture descriptor type</returns>
- public TextureDescriptorType UnpackTextureDescriptorType()
- {
- return (TextureDescriptorType)((Word2 >> 21) & 7);
- }
-
- /// <summary>
- /// Unpacks the texture stride (bytes per line) for linear textures only.
- /// Always 32-bytes aligned.
- /// </summary>
- /// <returns>The linear texture stride</returns>
- public int UnpackStride()
- {
- return (int)(Word3 & 0xffff) << 5;
- }
-
- /// <summary>
- /// Unpacks the GOB block size in X (width) for block linear textures.
- /// Must be always 1, ignored by the GPU.
- /// </summary>
- /// <returns>THe GOB block X size</returns>
- public int UnpackGobBlocksInX()
- {
- return 1 << (int)(Word3 & 7);
- }
-
- /// <summary>
- /// Unpacks the GOB block size in Y (height) for block linear textures.
- /// Must be always a power of 2, with a maximum value of 32.
- /// </summary>
- /// <returns>THe GOB block Y size</returns>
- public int UnpackGobBlocksInY()
- {
- return 1 << (int)((Word3 >> 3) & 7);
- }
-
- /// <summary>
- /// Unpacks the GOB block size in Z (depth) for block linear textures.
- /// Must be always a power of 2, with a maximum value of 32.
- /// Must be 1 for any texture target other than 3D textures.
- /// </summary>
- /// <returns>The GOB block Z size</returns>
- public int UnpackGobBlocksInZ()
- {
- return 1 << (int)((Word3 >> 6) & 7);
- }
-
- /// <summary>
- /// Number of GOB blocks per tile in the X direction.
- /// This is only used for sparse textures, should be 1 otherwise.
- /// </summary>
- /// <returns>The number of GOB blocks per tile</returns>
- public int UnpackGobBlocksInTileX()
- {
- return 1 << (int)((Word3 >> 10) & 7);
- }
-
- /// <summary>
- /// Unpacks the number of mipmap levels of the texture.
- /// </summary>
- /// <returns>The number of mipmap levels</returns>
- public int UnpackLevels()
- {
- return (int)(Word3 >> 28) + 1;
- }
-
- /// <summary>
- /// Unpack the base level texture width size.
- /// </summary>
- /// <returns>The texture width</returns>
- public int UnpackWidth()
- {
- return (int)(Word4 & 0xffff) + 1;
- }
-
- /// <summary>
- /// Unpack the width of a buffer texture.
- /// </summary>
- /// <returns>The texture width</returns>
- public int UnpackBufferTextureWidth()
- {
- return (int)((Word4 & 0xffff) | (Word3 << 16)) + 1;
- }
-
- /// <summary>
- /// Unpacks the texture sRGB format flag.
- /// </summary>
- /// <returns>True if the texture is sRGB, false otherwise</returns>
- public bool UnpackSrgb()
- {
- return (Word4 & (1 << 22)) != 0;
- }
-
- /// <summary>
- /// Unpacks the texture target.
- /// </summary>
- /// <returns>The texture target</returns>
- public TextureTarget UnpackTextureTarget()
- {
- return (TextureTarget)((Word4 >> 23) & 0xf);
- }
-
- /// <summary>
- /// Unpack the base level texture height size, or array layers for 1D array textures.
- /// Should be ignored for 1D or buffer textures.
- /// </summary>
- /// <returns>The texture height or layers count</returns>
- public int UnpackHeight()
- {
- return (int)(Word5 & 0xffff) + 1;
- }
-
- /// <summary>
- /// Unpack the base level texture depth size, number of array layers or cubemap faces.
- /// The meaning of this value depends on the texture target.
- /// </summary>
- /// <returns>The texture depth, layer or faces count</returns>
- public int UnpackDepth()
- {
- return (int)((Word5 >> 16) & 0x3fff) + 1;
- }
-
- /// <summary>
- /// Unpacks the texture coordinates normalized flag.
- /// When this is true, texture coordinates are expected to be in the [0, 1] range on the shader.
- /// When this is false, texture coordinates are expected to be in the [0, W], [0, H] and [0, D] range.
- /// It must be set to false (by the guest driver) for rectangle textures.
- /// </summary>
- /// <returns>The texture coordinates normalized flag</returns>
- public bool UnpackTextureCoordNormalized()
- {
- return (Word5 & (1 << 31)) != 0;
- }
-
- /// <summary>
- /// Unpacks the base mipmap level of the texture.
- /// </summary>
- /// <returns>The base mipmap level of the texture</returns>
- public int UnpackBaseLevel()
- {
- return (int)(Word7 & 0xf);
- }
-
- /// <summary>
- /// Unpacks the maximum mipmap level (inclusive) of the texture.
- /// Usually equal to Levels minus 1.
- /// </summary>
- /// <returns>The maximum mipmap level (inclusive) of the texture</returns>
- public int UnpackMaxLevelInclusive()
- {
- return (int)((Word7 >> 4) & 0xf);
- }
-
- /// <summary>
- /// Unpacks the multisampled texture samples count in each direction.
- /// Must be ignored for non-multisample textures.
- /// </summary>
- /// <returns>The multisample counts enum</returns>
- public TextureMsaaMode UnpackTextureMsaaMode()
- {
- return (TextureMsaaMode)((Word7 >> 8) & 0xf);
- }
-
- /// <summary>
- /// Check if two descriptors are equal.
- /// </summary>
- /// <param name="other">The descriptor to compare against</param>
- /// <returns>True if they are equal, false otherwise</returns>
- public bool Equals(ref TextureDescriptor other)
- {
- return Unsafe.As<TextureDescriptor, Vector256<byte>>(ref this).Equals(Unsafe.As<TextureDescriptor, Vector256<byte>>(ref other));
- }
-
- /// <summary>
- /// Check if two descriptors are equal.
- /// </summary>
- /// <param name="other">The descriptor to compare against</param>
- /// <returns>True if they are equal, false otherwise</returns>
- public bool Equals(TextureDescriptor other)
- {
- return Equals(ref other);
- }
-
- /// <summary>
- /// Gets a hash code for this descriptor.
- /// </summary>
- /// <returns>The hash code for this descriptor.</returns>
- public override int GetHashCode()
- {
- return Unsafe.As<TextureDescriptor, Vector256<byte>>(ref this).GetHashCode();
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
deleted file mode 100644
index 8e7d40bb..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// The texture descriptor type.
- /// This specifies the texture memory layout.
- /// The texture descriptor structure depends on the type.
- /// </summary>
- enum TextureDescriptorType
- {
- Buffer,
- LinearColorKey,
- Linear,
- BlockLinear,
- BlockLinearColorKey
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
deleted file mode 100644
index 234e7e8c..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
+++ /dev/null
@@ -1,1611 +0,0 @@
-using Ryujinx.Common.Memory;
-using Ryujinx.Cpu.Tracking;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Texture;
-using Ryujinx.Memory;
-using Ryujinx.Memory.Range;
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// An overlapping texture group with a given view compatibility.
- /// </summary>
- readonly struct TextureIncompatibleOverlap
- {
- public readonly TextureGroup Group;
- public readonly TextureViewCompatibility Compatibility;
-
- /// <summary>
- /// Create a new texture incompatible overlap.
- /// </summary>
- /// <param name="group">The group that is incompatible</param>
- /// <param name="compatibility">The view compatibility for the group</param>
- public TextureIncompatibleOverlap(TextureGroup group, TextureViewCompatibility compatibility)
- {
- Group = group;
- Compatibility = compatibility;
- }
- }
-
- /// <summary>
- /// A texture group represents a group of textures that belong to the same storage.
- /// When views are created, this class will track memory accesses for them separately.
- /// The group iteratively adds more granular tracking as views of different kinds are added.
- /// Note that a texture group can be absorbed into another when it becomes a view parent.
- /// </summary>
- class TextureGroup : IDisposable
- {
- /// <summary>
- /// Threshold of layers to force granular handles (and thus partial loading) on array/3D textures.
- /// </summary>
- private const int GranularLayerThreshold = 8;
-
- private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false);
-
- /// <summary>
- /// The storage texture associated with this group.
- /// </summary>
- public Texture Storage { get; }
-
- /// <summary>
- /// Indicates if the texture has copy dependencies. If true, then all modifications
- /// must be signalled to the group, rather than skipping ones still to be flushed.
- /// </summary>
- public bool HasCopyDependencies { get; set; }
-
- /// <summary>
- /// Indicates if this texture has any incompatible overlaps alive.
- /// </summary>
- public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0;
-
- private readonly GpuContext _context;
- private readonly PhysicalMemory _physicalMemory;
-
- private int[] _allOffsets;
- private int[] _sliceSizes;
- private bool _is3D;
- private bool _hasMipViews;
- private bool _hasLayerViews;
- private int _layers;
- private int _levels;
-
- private MultiRange TextureRange => Storage.Range;
-
- /// <summary>
- /// The views list from the storage texture.
- /// </summary>
- private List<Texture> _views;
- private TextureGroupHandle[] _handles;
- private bool[] _loadNeeded;
-
- /// <summary>
- /// Other texture groups that have incompatible overlaps with this one.
- /// </summary>
- private List<TextureIncompatibleOverlap> _incompatibleOverlaps;
- private bool _incompatibleOverlapsDirty = true;
- private bool _flushIncompatibleOverlaps;
-
- /// <summary>
- /// Create a new texture group.
- /// </summary>
- /// <param name="context">GPU context that the texture group belongs to</param>
- /// <param name="physicalMemory">Physical memory where the <paramref name="storage"/> texture is mapped</param>
- /// <param name="storage">The storage texture for this group</param>
- /// <param name="incompatibleOverlaps">Groups that overlap with this one but are incompatible</param>
- public TextureGroup(GpuContext context, PhysicalMemory physicalMemory, Texture storage, List<TextureIncompatibleOverlap> incompatibleOverlaps)
- {
- Storage = storage;
- _context = context;
- _physicalMemory = physicalMemory;
-
- _is3D = storage.Info.Target == Target.Texture3D;
- _layers = storage.Info.GetSlices();
- _levels = storage.Info.Levels;
-
- _incompatibleOverlaps = incompatibleOverlaps;
- _flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
- }
-
- /// <summary>
- /// Initialize a new texture group's dirty regions and offsets.
- /// </summary>
- /// <param name="size">Size info for the storage texture</param>
- /// <param name="hasLayerViews">True if the storage will have layer views</param>
- /// <param name="hasMipViews">True if the storage will have mip views</param>
- public void Initialize(ref SizeInfo size, bool hasLayerViews, bool hasMipViews)
- {
- _allOffsets = size.AllOffsets;
- _sliceSizes = size.SliceSizes;
-
- if (Storage.Target.HasDepthOrLayers() && Storage.Info.GetSlices() > GranularLayerThreshold)
- {
- _hasLayerViews = true;
- _hasMipViews = true;
- }
- else
- {
- (_hasLayerViews, _hasMipViews) = PropagateGranularity(hasLayerViews, hasMipViews);
-
- // If the texture is partially mapped, fully subdivide handles immediately.
-
- MultiRange range = Storage.Range;
- for (int i = 0; i < range.Count; i++)
- {
- if (range.GetSubRange(i).Address == MemoryManager.PteUnmapped)
- {
- _hasLayerViews = true;
- _hasMipViews = true;
-
- break;
- }
- }
- }
-
- RecalculateHandleRegions();
- }
-
- /// <summary>
- /// Initialize all incompatible overlaps in the list, registering them with the other texture groups
- /// and creating copy dependencies when partially compatible.
- /// </summary>
- public void InitializeOverlaps()
- {
- foreach (TextureIncompatibleOverlap overlap in _incompatibleOverlaps)
- {
- if (overlap.Compatibility == TextureViewCompatibility.LayoutIncompatible)
- {
- CreateCopyDependency(overlap.Group, false);
- }
-
- overlap.Group._incompatibleOverlaps.Add(new TextureIncompatibleOverlap(this, overlap.Compatibility));
- overlap.Group._incompatibleOverlapsDirty = true;
- }
-
- if (_incompatibleOverlaps.Count > 0)
- {
- SignalIncompatibleOverlapModified();
- }
- }
-
- /// <summary>
- /// Signal that the group is dirty to all views and the storage.
- /// </summary>
- private void SignalAllDirty()
- {
- Storage.SignalGroupDirty();
- if (_views != null)
- {
- foreach (Texture texture in _views)
- {
- texture.SignalGroupDirty();
- }
- }
- }
-
- /// <summary>
- /// Signal that an incompatible overlap has been modified.
- /// If this group must flush incompatible overlaps, the group is signalled as dirty too.
- /// </summary>
- private void SignalIncompatibleOverlapModified()
- {
- _incompatibleOverlapsDirty = true;
-
- if (_flushIncompatibleOverlaps)
- {
- SignalAllDirty();
- }
- }
-
-
- /// <summary>
- /// Flushes incompatible overlaps if the storage format requires it, and they have been modified.
- /// This allows unsupported host formats to accept data written to format aliased textures.
- /// </summary>
- /// <returns>True if data was flushed, false otherwise</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool FlushIncompatibleOverlapsIfNeeded()
- {
- if (_flushIncompatibleOverlaps && _incompatibleOverlapsDirty)
- {
- bool flushed = false;
-
- foreach (var overlap in _incompatibleOverlaps)
- {
- flushed |= overlap.Group.Storage.FlushModified(true);
- }
-
- _incompatibleOverlapsDirty = false;
-
- return flushed;
- }
- else
- {
- return false;
- }
- }
-
- /// <summary>
- /// Check and optionally consume the dirty flags for a given texture.
- /// The state is shared between views of the same layers and levels.
- /// </summary>
- /// <param name="texture">The texture being used</param>
- /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
- /// <returns>True if a flag was dirty, false otherwise</returns>
- public bool CheckDirty(Texture texture, bool consume)
- {
- bool dirty = false;
-
- EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
- {
- for (int i = 0; i < regionCount; i++)
- {
- TextureGroupHandle group = _handles[baseHandle + i];
-
- foreach (CpuRegionHandle handle in group.Handles)
- {
- if (handle.Dirty)
- {
- if (consume)
- {
- handle.Reprotect();
- }
-
- dirty = true;
- }
- }
- }
- });
-
- return dirty;
- }
-
- /// <summary>
- /// Synchronize memory for a given texture.
- /// If overlapping tracking handles are dirty, fully or partially synchronize the texture data.
- /// </summary>
- /// <param name="texture">The texture being used</param>
- public void SynchronizeMemory(Texture texture)
- {
- FlushIncompatibleOverlapsIfNeeded();
-
- EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
- {
- bool dirty = false;
- bool anyModified = false;
- bool anyNotDirty = false;
-
- for (int i = 0; i < regionCount; i++)
- {
- TextureGroupHandle group = _handles[baseHandle + i];
-
- bool modified = group.Modified;
- bool handleDirty = false;
- bool handleUnmapped = false;
-
- foreach (CpuRegionHandle handle in group.Handles)
- {
- if (handle.Dirty)
- {
- handle.Reprotect();
- handleDirty = true;
- }
- else
- {
- handleUnmapped |= handle.Unmapped;
- }
- }
-
- // If the modified flag is still present, prefer the data written from gpu.
- // A write from CPU will do a flush before writing its data, which should unset this.
- if (modified)
- {
- handleDirty = false;
- }
-
- // Evaluate if any copy dependencies need to be fulfilled. A few rules:
- // If the copy handle needs to be synchronized, prefer our own state.
- // If we need to be synchronized and there is a copy present, prefer the copy.
-
- if (group.NeedsCopy && group.Copy(_context))
- {
- anyModified |= true; // The copy target has been modified.
- handleDirty = false;
- }
- else
- {
- anyModified |= modified;
- dirty |= handleDirty;
- }
-
- if (group.NeedsCopy)
- {
- // The texture we copied from is still being written to. Copy from it again the next time this texture is used.
- texture.SignalGroupDirty();
- }
-
- bool loadNeeded = handleDirty && !handleUnmapped;
-
- anyNotDirty |= !loadNeeded;
- _loadNeeded[baseHandle + i] = loadNeeded;
- }
-
- if (dirty)
- {
- if (anyNotDirty || (_handles.Length > 1 && (anyModified || split)))
- {
- // Partial texture invalidation. Only update the layers/levels with dirty flags of the storage.
-
- SynchronizePartial(baseHandle, regionCount);
- }
- else
- {
- // Full texture invalidation.
-
- texture.SynchronizeFull();
- }
- }
- });
- }
-
- /// <summary>
- /// Synchronize part of the storage texture, represented by a given range of handles.
- /// Only handles marked by the _loadNeeded array will be synchronized.
- /// </summary>
- /// <param name="baseHandle">The base index of the range of handles</param>
- /// <param name="regionCount">The number of handles to synchronize</param>
- private void SynchronizePartial(int baseHandle, int regionCount)
- {
- int spanEndIndex = -1;
- int spanBase = 0;
- ReadOnlySpan<byte> dataSpan = ReadOnlySpan<byte>.Empty;
-
- for (int i = 0; i < regionCount; i++)
- {
- if (_loadNeeded[baseHandle + i])
- {
- var info = GetHandleInformation(baseHandle + i);
-
- // Ensure the data for this handle is loaded in the span.
- if (spanEndIndex <= i - 1)
- {
- spanEndIndex = i;
-
- if (_is3D)
- {
- // Look ahead to see how many handles need to be loaded.
- for (int j = i + 1; j < regionCount; j++)
- {
- if (_loadNeeded[baseHandle + j])
- {
- spanEndIndex = j;
- }
- else
- {
- break;
- }
- }
- }
-
- var endInfo = spanEndIndex == i ? info : GetHandleInformation(baseHandle + spanEndIndex);
-
- spanBase = _allOffsets[info.Index];
- int spanLast = _allOffsets[endInfo.Index + endInfo.Layers * endInfo.Levels - 1];
- int endOffset = Math.Min(spanLast + _sliceSizes[endInfo.BaseLevel + endInfo.Levels - 1], (int)Storage.Size);
- int size = endOffset - spanBase;
-
- dataSpan = _physicalMemory.GetSpan(Storage.Range.Slice((ulong)spanBase, (ulong)size));
- }
-
- // Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
- for (int layer = 0; layer < info.Layers; layer++)
- {
- for (int level = 0; level < info.Levels; level++)
- {
- int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
- int offset = _allOffsets[offsetIndex];
-
- ReadOnlySpan<byte> data = dataSpan.Slice(offset - spanBase);
-
- SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
-
- Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
- }
- }
- }
- }
- }
-
- /// <summary>
- /// Synchronize dependent textures, if any of them have deferred a copy from the given texture.
- /// </summary>
- /// <param name="texture">The texture to synchronize dependents of</param>
- public void SynchronizeDependents(Texture texture)
- {
- EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
- {
- for (int i = 0; i < regionCount; i++)
- {
- TextureGroupHandle group = _handles[baseHandle + i];
-
- group.SynchronizeDependents();
- }
- });
- }
-
- /// <summary>
- /// Determines whether flushes in this texture group should be tracked.
- /// Incompatible overlaps may need data from this texture to flush tracked for it to be visible to them.
- /// </summary>
- /// <returns>True if flushes should be tracked, false otherwise</returns>
- private bool ShouldFlushTriggerTracking()
- {
- foreach (var overlap in _incompatibleOverlaps)
- {
- if (overlap.Group._flushIncompatibleOverlaps)
- {
- return true;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Gets data from the host GPU, and flushes a slice to guest memory.
- /// </summary>
- /// <remarks>
- /// This method should be used to retrieve data that was modified by the host GPU.
- /// This is not cheap, avoid doing that unless strictly needed.
- /// When possible, the data is written directly into guest memory, rather than copied.
- /// </remarks>
- /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
- /// <param name="sliceIndex">The index of the slice to flush</param>
- /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
- private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, ITexture texture = null)
- {
- (int layer, int level) = GetLayerLevelForView(sliceIndex);
-
- int offset = _allOffsets[sliceIndex];
- int endOffset = Math.Min(offset + _sliceSizes[level], (int)Storage.Size);
- int size = endOffset - offset;
-
- using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked);
-
- Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture);
- }
-
- /// <summary>
- /// Gets and flushes a number of slices of the storage texture to guest memory.
- /// </summary>
- /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
- /// <param name="sliceStart">The first slice to flush</param>
- /// <param name="sliceEnd">The slice to finish flushing on (exclusive)</param>
- /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
- private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, ITexture texture = null)
- {
- for (int i = sliceStart; i < sliceEnd; i++)
- {
- FlushTextureDataSliceToGuest(tracked, i, texture);
- }
- }
-
- /// <summary>
- /// Flush modified ranges for a given texture.
- /// </summary>
- /// <param name="texture">The texture being used</param>
- /// <param name="tracked">True if the flush writes should be tracked, false otherwise</param>
- /// <returns>True if data was flushed, false otherwise</returns>
- public bool FlushModified(Texture texture, bool tracked)
- {
- tracked = tracked || ShouldFlushTriggerTracking();
- bool flushed = false;
-
- EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
- {
- int startSlice = 0;
- int endSlice = 0;
- bool allModified = true;
-
- for (int i = 0; i < regionCount; i++)
- {
- TextureGroupHandle group = _handles[baseHandle + i];
-
- if (group.Modified)
- {
- if (endSlice < group.BaseSlice)
- {
- if (endSlice > startSlice)
- {
- FlushSliceRange(tracked, startSlice, endSlice);
- flushed = true;
- }
-
- startSlice = group.BaseSlice;
- }
-
- endSlice = group.BaseSlice + group.SliceCount;
-
- if (tracked)
- {
- group.Modified = false;
-
- foreach (Texture texture in group.Overlaps)
- {
- texture.SignalModifiedDirty();
- }
- }
- }
- else
- {
- allModified = false;
- }
- }
-
- if (endSlice > startSlice)
- {
- if (allModified && !split)
- {
- texture.Flush(tracked);
- }
- else
- {
- FlushSliceRange(tracked, startSlice, endSlice);
- }
-
- flushed = true;
- }
- });
-
- Storage.SignalModifiedDirty();
-
- return flushed;
- }
-
- /// <summary>
- /// Clears competing modified flags for all incompatible ranges, if they have possibly been modified.
- /// </summary>
- /// <param name="texture">The texture that has been modified</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ClearIncompatibleOverlaps(Texture texture)
- {
- if (_incompatibleOverlapsDirty)
- {
- foreach (TextureIncompatibleOverlap incompatible in _incompatibleOverlaps)
- {
- incompatible.Group.ClearModified(texture.Range, this);
-
- incompatible.Group.SignalIncompatibleOverlapModified();
- }
-
- _incompatibleOverlapsDirty = false;
- }
- }
-
- /// <summary>
- /// Signal that a texture in the group has been modified by the GPU.
- /// </summary>
- /// <param name="texture">The texture that has been modified</param>
- public void SignalModified(Texture texture)
- {
- ClearIncompatibleOverlaps(texture);
-
- EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
- {
- for (int i = 0; i < regionCount; i++)
- {
- TextureGroupHandle group = _handles[baseHandle + i];
-
- group.SignalModified(_context);
- }
- });
- }
-
- /// <summary>
- /// Signal that a texture in the group is actively bound, or has been unbound by the GPU.
- /// </summary>
- /// <param name="texture">The texture that has been modified</param>
- /// <param name="bound">True if this texture is being bound, false if unbound</param>
- public void SignalModifying(Texture texture, bool bound)
- {
- ClearIncompatibleOverlaps(texture);
-
- EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
- {
- for (int i = 0; i < regionCount; i++)
- {
- TextureGroupHandle group = _handles[baseHandle + i];
-
- group.SignalModifying(bound, _context);
- }
- });
- }
-
- /// <summary>
- /// Register a read/write action to flush for a texture group.
- /// </summary>
- /// <param name="group">The group to register an action for</param>
- public void RegisterAction(TextureGroupHandle group)
- {
- foreach (CpuRegionHandle handle in group.Handles)
- {
- handle.RegisterAction((address, size) => FlushAction(group, address, size));
- }
- }
-
- /// <summary>
- /// Propagates the mip/layer view flags depending on the texture type.
- /// When the most granular type of subresource has views, the other type of subresource must be segmented granularly too.
- /// </summary>
- /// <param name="hasLayerViews">True if the storage has layer views</param>
- /// <param name="hasMipViews">True if the storage has mip views</param>
- /// <returns>The input values after propagation</returns>
- private (bool HasLayerViews, bool HasMipViews) PropagateGranularity(bool hasLayerViews, bool hasMipViews)
- {
- if (_is3D)
- {
- hasMipViews |= hasLayerViews;
- }
- else
- {
- hasLayerViews |= hasMipViews;
- }
-
- return (hasLayerViews, hasMipViews);
- }
-
- /// <summary>
- /// Evaluate the range of tracking handles which a view texture overlaps with.
- /// </summary>
- /// <param name="texture">The texture to get handles for</param>
- /// <param name="callback">
- /// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
- /// This can be called for multiple disjoint ranges, if required.
- /// </param>
- private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback)
- {
- if (texture == Storage || !(_hasMipViews || _hasLayerViews))
- {
- callback(0, _handles.Length);
-
- return;
- }
-
- EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback);
- }
-
- /// <summary>
- /// Evaluate the range of tracking handles which a view texture overlaps with,
- /// using the view's position and slice/level counts.
- /// </summary>
- /// <param name="firstLayer">The first layer of the texture</param>
- /// <param name="firstLevel">The first level of the texture</param>
- /// <param name="slices">The slice count of the texture</param>
- /// <param name="levels">The level count of the texture</param>
- /// <param name="callback">
- /// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
- /// This can be called for multiple disjoint ranges, if required.
- /// </param>
- private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback)
- {
- int targetLayerHandles = _hasLayerViews ? slices : 1;
- int targetLevelHandles = _hasMipViews ? levels : 1;
-
- if (_is3D)
- {
- // Future mip levels come after all layers of the last mip level. Each mipmap has less layers (depth) than the last.
-
- if (!_hasLayerViews)
- {
- // When there are no layer views, the mips are at a consistent offset.
-
- callback(firstLevel, targetLevelHandles);
- }
- else
- {
- (int levelIndex, int layerCount) = Get3DLevelRange(firstLevel);
-
- if (levels > 1 && slices < _layers)
- {
- // The given texture only covers some of the depth of multiple mips. (a "depth slice")
- // Callback with each mip's range separately.
- // Can assume that the group is fully subdivided (both slices and levels > 1 for storage)
-
- while (levels-- > 1)
- {
- callback(firstLayer + levelIndex, slices);
-
- levelIndex += layerCount;
- layerCount = Math.Max(layerCount >> 1, 1);
- slices = Math.Max(layerCount >> 1, 1);
- }
- }
- else
- {
- int totalSize = Math.Min(layerCount, slices);
-
- while (levels-- > 1)
- {
- layerCount = Math.Max(layerCount >> 1, 1);
- totalSize += layerCount;
- }
-
- callback(firstLayer + levelIndex, totalSize);
- }
- }
- }
- else
- {
- // Future layers come after all mipmaps of the last.
- int levelHandles = _hasMipViews ? _levels : 1;
-
- if (slices > 1 && levels < _levels)
- {
- // The given texture only covers some of the mipmaps of multiple slices. (a "mip slice")
- // Callback with each layer's range separately.
- // Can assume that the group is fully subdivided (both slices and levels > 1 for storage)
-
- for (int i = 0; i < slices; i++)
- {
- callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true);
- }
- }
- else
- {
- callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles);
- }
- }
- }
-
- /// <summary>
- /// Get the range of offsets for a given mip level of a 3D texture.
- /// </summary>
- /// <param name="level">The level to return</param>
- /// <returns>Start index and count of offsets for the given level</returns>
- private (int Index, int Count) Get3DLevelRange(int level)
- {
- int index = 0;
- int count = _layers; // Depth. Halves with each mip level.
-
- while (level-- > 0)
- {
- index += count;
- count = Math.Max(count >> 1, 1);
- }
-
- return (index, count);
- }
-
- /// <summary>
- /// Get view information for a single tracking handle.
- /// </summary>
- /// <param name="handleIndex">The index of the handle</param>
- /// <returns>The layers and levels that the handle covers, and its index in the offsets array</returns>
- private (int BaseLayer, int BaseLevel, int Levels, int Layers, int Index) GetHandleInformation(int handleIndex)
- {
- int baseLayer;
- int baseLevel;
- int levels = _hasMipViews ? 1 : _levels;
- int layers = _hasLayerViews ? 1 : _layers;
- int index;
-
- if (_is3D)
- {
- if (_hasLayerViews)
- {
- // NOTE: Will also have mip views, or only one level in storage.
-
- index = handleIndex;
- baseLevel = 0;
-
- int levelLayers = _layers;
-
- while (handleIndex >= levelLayers)
- {
- handleIndex -= levelLayers;
- baseLevel++;
- levelLayers = Math.Max(levelLayers >> 1, 1);
- }
-
- baseLayer = handleIndex;
- }
- else
- {
- baseLayer = 0;
- baseLevel = handleIndex;
-
- (index, _) = Get3DLevelRange(baseLevel);
- }
- }
- else
- {
- baseLevel = _hasMipViews ? handleIndex % _levels : 0;
- baseLayer = _hasMipViews ? handleIndex / _levels : handleIndex;
- index = baseLevel + baseLayer * _levels;
- }
-
- return (baseLayer, baseLevel, levels, layers, index);
- }
-
- /// <summary>
- /// Gets the layer and level for a given view.
- /// </summary>
- /// <param name="index">The index of the view</param>
- /// <returns>The layer and level of the specified view</returns>
- private (int BaseLayer, int BaseLevel) GetLayerLevelForView(int index)
- {
- if (_is3D)
- {
- int baseLevel = 0;
-
- int levelLayers = _layers;
-
- while (index >= levelLayers)
- {
- index -= levelLayers;
- baseLevel++;
- levelLayers = Math.Max(levelLayers >> 1, 1);
- }
-
- return (index, baseLevel);
- }
- else
- {
- return (index / _levels, index % _levels);
- }
- }
-
- /// <summary>
- /// Find the byte offset of a given texture relative to the storage.
- /// </summary>
- /// <param name="texture">The texture to locate</param>
- /// <returns>The offset of the texture in bytes</returns>
- public int FindOffset(Texture texture)
- {
- return _allOffsets[GetOffsetIndex(texture.FirstLayer, texture.FirstLevel)];
- }
-
- /// <summary>
- /// Find the offset index of a given layer and level.
- /// </summary>
- /// <param name="layer">The view layer</param>
- /// <param name="level">The view level</param>
- /// <returns>The offset index of the given layer and level</returns>
- public int GetOffsetIndex(int layer, int level)
- {
- if (_is3D)
- {
- return layer + Get3DLevelRange(level).Index;
- }
- else
- {
- return level + layer * _levels;
- }
- }
-
- /// <summary>
- /// The action to perform when a memory tracking handle is flipped to dirty.
- /// This notifies overlapping textures that the memory needs to be synchronized.
- /// </summary>
- /// <param name="groupHandle">The handle that a dirty flag was set on</param>
- private void DirtyAction(TextureGroupHandle groupHandle)
- {
- // Notify all textures that belong to this handle.
-
- Storage.SignalGroupDirty();
-
- lock (groupHandle.Overlaps)
- {
- foreach (Texture overlap in groupHandle.Overlaps)
- {
- overlap.SignalGroupDirty();
- }
- }
- }
-
- /// <summary>
- /// Generate a CpuRegionHandle for a given address and size range in CPU VA.
- /// </summary>
- /// <param name="address">The start address of the tracked region</param>
- /// <param name="size">The size of the tracked region</param>
- /// <returns>A CpuRegionHandle covering the given range</returns>
- private CpuRegionHandle GenerateHandle(ulong address, ulong size)
- {
- return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture);
- }
-
- /// <summary>
- /// Generate a TextureGroupHandle covering a specified range of views.
- /// </summary>
- /// <param name="viewStart">The start view of the handle</param>
- /// <param name="views">The number of views to cover</param>
- /// <returns>A TextureGroupHandle covering the given views</returns>
- private TextureGroupHandle GenerateHandles(int viewStart, int views)
- {
- int viewEnd = viewStart + views - 1;
- (_, int lastLevel) = GetLayerLevelForView(viewEnd);
-
- int offset = _allOffsets[viewStart];
- int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel];
- int size = endOffset - offset;
-
- var result = new List<CpuRegionHandle>();
-
- for (int i = 0; i < TextureRange.Count; i++)
- {
- MemoryRange item = TextureRange.GetSubRange(i);
- int subRangeSize = (int)item.Size;
-
- int sliceStart = Math.Clamp(offset, 0, subRangeSize);
- int sliceEnd = Math.Clamp(endOffset, 0, subRangeSize);
-
- if (sliceStart != sliceEnd && item.Address != MemoryManager.PteUnmapped)
- {
- result.Add(GenerateHandle(item.Address + (ulong)sliceStart, (ulong)(sliceEnd - sliceStart)));
- }
-
- offset -= subRangeSize;
- endOffset -= subRangeSize;
-
- if (endOffset <= 0)
- {
- break;
- }
- }
-
- (int firstLayer, int firstLevel) = GetLayerLevelForView(viewStart);
-
- if (_hasLayerViews && _hasMipViews)
- {
- size = _sliceSizes[firstLevel];
- }
-
- offset = _allOffsets[viewStart];
- ulong maxSize = Storage.Size - (ulong)offset;
-
- var groupHandle = new TextureGroupHandle(
- this,
- offset,
- Math.Min(maxSize, (ulong)size),
- _views,
- firstLayer,
- firstLevel,
- viewStart,
- views,
- result.ToArray());
-
- foreach (CpuRegionHandle handle in result)
- {
- handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
- }
-
- return groupHandle;
- }
-
- /// <summary>
- /// Update the views in this texture group, rebuilding the memory tracking if required.
- /// </summary>
- /// <param name="views">The views list of the storage texture</param>
- /// <param name="texture">The texture that has been added, if that is the only change, otherwise null</param>
- public void UpdateViews(List<Texture> views, Texture texture)
- {
- // This is saved to calculate overlapping views for each handle.
- _views = views;
-
- bool layerViews = _hasLayerViews;
- bool mipViews = _hasMipViews;
- bool regionsRebuilt = false;
-
- if (!(layerViews && mipViews))
- {
- foreach (Texture view in views)
- {
- if (view.Info.GetSlices() < _layers)
- {
- layerViews = true;
- }
-
- if (view.Info.Levels < _levels)
- {
- mipViews = true;
- }
- }
-
- (layerViews, mipViews) = PropagateGranularity(layerViews, mipViews);
-
- if (layerViews != _hasLayerViews || mipViews != _hasMipViews)
- {
- _hasLayerViews = layerViews;
- _hasMipViews = mipViews;
-
- RecalculateHandleRegions();
- regionsRebuilt = true;
- }
- }
-
- if (!regionsRebuilt)
- {
- if (texture != null)
- {
- int offset = FindOffset(texture);
-
- foreach (TextureGroupHandle handle in _handles)
- {
- handle.AddOverlap(offset, texture);
- }
- }
- else
- {
- // Must update the overlapping views on all handles, but only if they were not just recreated.
-
- foreach (TextureGroupHandle handle in _handles)
- {
- handle.RecalculateOverlaps(this, views);
- }
- }
- }
-
- SignalAllDirty();
- }
-
-
- /// <summary>
- /// Removes a view from the group, removing it from all overlap lists.
- /// </summary>
- /// <param name="view">View to remove from the group</param>
- public void RemoveView(Texture view)
- {
- int offset = FindOffset(view);
-
- foreach (TextureGroupHandle handle in _handles)
- {
- handle.RemoveOverlap(offset, view);
- }
- }
-
- /// <summary>
- /// Inherit handle state from an old set of handles, such as modified and dirty flags.
- /// </summary>
- /// <param name="oldHandles">The set of handles to inherit state from</param>
- /// <param name="handles">The set of handles inheriting the state</param>
- /// <param name="relativeOffset">The offset of the old handles in relation to the new ones</param>
- private void InheritHandles(TextureGroupHandle[] oldHandles, TextureGroupHandle[] handles, int relativeOffset)
- {
- foreach (var group in handles)
- {
- foreach (var handle in group.Handles)
- {
- bool dirty = false;
-
- foreach (var oldGroup in oldHandles)
- {
- if (group.OverlapsWith(oldGroup.Offset + relativeOffset, oldGroup.Size))
- {
- foreach (var oldHandle in oldGroup.Handles)
- {
- if (handle.OverlapsWith(oldHandle.Address, oldHandle.Size))
- {
- dirty |= oldHandle.Dirty;
- }
- }
-
- group.Inherit(oldGroup, group.Offset == oldGroup.Offset + relativeOffset);
- }
- }
-
- if (dirty && !handle.Dirty)
- {
- handle.Reprotect(true);
- }
-
- if (group.Modified)
- {
- handle.RegisterAction((address, size) => FlushAction(group, address, size));
- }
- }
- }
-
- foreach (var oldGroup in oldHandles)
- {
- oldGroup.Modified = false;
- }
- }
-
- /// <summary>
- /// Inherit state from another texture group.
- /// </summary>
- /// <param name="other">The texture group to inherit from</param>
- public void Inherit(TextureGroup other)
- {
- bool layerViews = _hasLayerViews || other._hasLayerViews;
- bool mipViews = _hasMipViews || other._hasMipViews;
-
- if (layerViews != _hasLayerViews || mipViews != _hasMipViews)
- {
- _hasLayerViews = layerViews;
- _hasMipViews = mipViews;
-
- RecalculateHandleRegions();
- }
-
- foreach (TextureIncompatibleOverlap incompatible in other._incompatibleOverlaps)
- {
- RegisterIncompatibleOverlap(incompatible, false);
-
- incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == other);
- }
-
- int relativeOffset = Storage.Range.FindOffset(other.Storage.Range);
-
- InheritHandles(other._handles, _handles, relativeOffset);
- }
-
- /// <summary>
- /// Replace the current handles with the new handles. It is assumed that the new handles start dirty.
- /// The dirty flags from the previous handles will be kept.
- /// </summary>
- /// <param name="handles">The handles to replace the current handles with</param>
- /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param>
- private void ReplaceHandles(TextureGroupHandle[] handles, bool rangeChanged)
- {
- if (_handles != null)
- {
- // When replacing handles, they should start as non-dirty.
-
- foreach (TextureGroupHandle groupHandle in handles)
- {
- if (rangeChanged)
- {
- // When the storage range changes, this becomes a little different.
- // If a range does not match one in the original, treat it as modified.
- // It has been newly mapped and its data must be synchronized.
-
- if (groupHandle.Handles.Length == 0)
- {
- continue;
- }
-
- foreach (var oldGroup in _handles)
- {
- if (!groupHandle.OverlapsWith(oldGroup.Offset, oldGroup.Size))
- {
- continue;
- }
-
- foreach (CpuRegionHandle handle in groupHandle.Handles)
- {
- bool hasMatch = false;
-
- foreach (var oldHandle in oldGroup.Handles)
- {
- if (oldHandle.RangeEquals(handle))
- {
- hasMatch = true;
- break;
- }
- }
-
- if (hasMatch)
- {
- handle.Reprotect();
- }
- }
- }
- }
- else
- {
- foreach (CpuRegionHandle handle in groupHandle.Handles)
- {
- handle.Reprotect();
- }
- }
- }
-
- InheritHandles(_handles, handles, 0);
-
- foreach (var oldGroup in _handles)
- {
- foreach (var oldHandle in oldGroup.Handles)
- {
- oldHandle.Dispose();
- }
- }
- }
-
- _handles = handles;
- _loadNeeded = new bool[_handles.Length];
- }
-
- /// <summary>
- /// Recalculate handle regions for this texture group, and inherit existing state into the new handles.
- /// </summary>
- /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param>
- private void RecalculateHandleRegions(bool rangeChanged = false)
- {
- TextureGroupHandle[] handles;
-
- if (!(_hasMipViews || _hasLayerViews))
- {
- // Single dirty region.
- var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count];
- int count = 0;
-
- for (int i = 0; i < TextureRange.Count; i++)
- {
- var currentRange = TextureRange.GetSubRange(i);
- if (currentRange.Address != MemoryManager.PteUnmapped)
- {
- cpuRegionHandles[count++] = GenerateHandle(currentRange.Address, currentRange.Size);
- }
- }
-
- if (count != TextureRange.Count)
- {
- Array.Resize(ref cpuRegionHandles, count);
- }
-
- var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
-
- foreach (CpuRegionHandle handle in cpuRegionHandles)
- {
- handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
- }
-
- handles = new TextureGroupHandle[] { groupHandle };
- }
- else
- {
- // Get views for the host texture.
- // It's worth noting that either the texture has layer views or mip views when getting to this point, which simplifies the logic a little.
- // Depending on if the texture is 3d, either the mip views imply that layer views are present (2d) or the other way around (3d).
- // This is enforced by the way the texture matched as a view, so we don't need to check.
-
- int layerHandles = _hasLayerViews ? _layers : 1;
- int levelHandles = _hasMipViews ? _levels : 1;
-
- int handleIndex = 0;
-
- if (_is3D)
- {
- var handlesList = new List<TextureGroupHandle>();
-
- for (int i = 0; i < levelHandles; i++)
- {
- for (int j = 0; j < layerHandles; j++)
- {
- (int viewStart, int views) = Get3DLevelRange(i);
- viewStart += j;
- views = _hasLayerViews ? 1 : views; // A layer view is also a mip view.
-
- handlesList.Add(GenerateHandles(viewStart, views));
- }
-
- layerHandles = Math.Max(1, layerHandles >> 1);
- }
-
- handles = handlesList.ToArray();
- }
- else
- {
- handles = new TextureGroupHandle[layerHandles * levelHandles];
-
- for (int i = 0; i < layerHandles; i++)
- {
- for (int j = 0; j < levelHandles; j++)
- {
- int viewStart = j + i * _levels;
- int views = _hasMipViews ? 1 : _levels; // A mip view is also a layer view.
-
- handles[handleIndex++] = GenerateHandles(viewStart, views);
- }
- }
- }
- }
-
- ReplaceHandles(handles, rangeChanged);
- }
-
- /// <summary>
- /// Regenerates handles when the storage range has been remapped.
- /// This forces the regions to be fully subdivided.
- /// </summary>
- public void RangeChanged()
- {
- _hasLayerViews = true;
- _hasMipViews = true;
-
- RecalculateHandleRegions(true);
-
- SignalAllDirty();
- }
-
- /// <summary>
- /// Ensure that there is a handle for each potential texture view. Required for copy dependencies to work.
- /// </summary>
- private void EnsureFullSubdivision()
- {
- if (!(_hasLayerViews && _hasMipViews))
- {
- _hasLayerViews = true;
- _hasMipViews = true;
-
- RecalculateHandleRegions();
- }
- }
-
- /// <summary>
- /// Create a copy dependency between this texture group, and a texture at a given layer/level offset.
- /// </summary>
- /// <param name="other">The view compatible texture to create a dependency to</param>
- /// <param name="firstLayer">The base layer of the given texture relative to the storage</param>
- /// <param name="firstLevel">The base level of the given texture relative to the storage</param>
- /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
- public void CreateCopyDependency(Texture other, int firstLayer, int firstLevel, bool copyTo)
- {
- TextureGroup otherGroup = other.Group;
-
- EnsureFullSubdivision();
- otherGroup.EnsureFullSubdivision();
-
- // Get the location of each texture within its storage, so we can find the handles to apply the dependency to.
- // This can consist of multiple disjoint regions, for example if this is a mip slice of an array texture.
-
- var targetRange = new List<(int BaseHandle, int RegionCount)>();
- var otherRange = new List<(int BaseHandle, int RegionCount)>();
-
- EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split) => targetRange.Add((baseHandle, regionCount)));
- otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split) => otherRange.Add((baseHandle, regionCount)));
-
- int targetIndex = 0;
- int otherIndex = 0;
- (int Handle, int RegionCount) targetRegion = (0, 0);
- (int Handle, int RegionCount) otherRegion = (0, 0);
-
- while (true)
- {
- if (targetRegion.RegionCount == 0)
- {
- if (targetIndex >= targetRange.Count)
- {
- break;
- }
-
- targetRegion = targetRange[targetIndex++];
- }
-
- if (otherRegion.RegionCount == 0)
- {
- if (otherIndex >= otherRange.Count)
- {
- break;
- }
-
- otherRegion = otherRange[otherIndex++];
- }
-
- TextureGroupHandle handle = _handles[targetRegion.Handle++];
- TextureGroupHandle otherHandle = other.Group._handles[otherRegion.Handle++];
-
- targetRegion.RegionCount--;
- otherRegion.RegionCount--;
-
- handle.CreateCopyDependency(otherHandle, copyTo);
-
- // If "copyTo" is true, this texture must copy to the other.
- // Otherwise, it must copy to this texture.
-
- if (copyTo)
- {
- otherHandle.Copy(_context, handle);
- }
- else
- {
- handle.Copy(_context, otherHandle);
- }
- }
- }
-
- /// <summary>
- /// Creates a copy dependency to another texture group, where handles overlap.
- /// Scans through all handles to find compatible patches in the other group.
- /// </summary>
- /// <param name="other">The texture group that overlaps this one</param>
- /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
- public void CreateCopyDependency(TextureGroup other, bool copyTo)
- {
- for (int i = 0; i < _allOffsets.Length; i++)
- {
- (int layer, int level) = GetLayerLevelForView(i);
- MultiRange handleRange = Storage.Range.Slice((ulong)_allOffsets[i], 1);
- ulong handleBase = handleRange.GetSubRange(0).Address;
-
- for (int j = 0; j < other._handles.Length; j++)
- {
- (int otherLayer, int otherLevel) = other.GetLayerLevelForView(j);
- MultiRange otherHandleRange = other.Storage.Range.Slice((ulong)other._allOffsets[j], 1);
- ulong otherHandleBase = otherHandleRange.GetSubRange(0).Address;
-
- if (handleBase == otherHandleBase)
- {
- // Check if the two sizes are compatible.
- TextureInfo info = Storage.Info;
- TextureInfo otherInfo = other.Storage.Info;
-
- if (TextureCompatibility.ViewLayoutCompatible(info, otherInfo, level, otherLevel) &&
- TextureCompatibility.CopySizeMatches(info, otherInfo, level, otherLevel))
- {
- // These textures are copy compatible. Create the dependency.
-
- EnsureFullSubdivision();
- other.EnsureFullSubdivision();
-
- TextureGroupHandle handle = _handles[i];
- TextureGroupHandle otherHandle = other._handles[j];
-
- handle.CreateCopyDependency(otherHandle, copyTo);
-
- // If "copyTo" is true, this texture must copy to the other.
- // Otherwise, it must copy to this texture.
-
- if (copyTo)
- {
- otherHandle.Copy(_context, handle);
- }
- else
- {
- handle.Copy(_context, otherHandle);
- }
- }
- }
- }
- }
- }
-
- /// <summary>
- /// Registers another texture group as an incompatible overlap, if not already registered.
- /// </summary>
- /// <param name="other">The texture group to add to the incompatible overlaps list</param>
- /// <param name="copy">True if the overlap should register copy dependencies</param>
- public void RegisterIncompatibleOverlap(TextureIncompatibleOverlap other, bool copy)
- {
- if (!_incompatibleOverlaps.Exists(overlap => overlap.Group == other.Group))
- {
- if (copy && other.Compatibility == TextureViewCompatibility.LayoutIncompatible)
- {
- // Any of the group's views may share compatibility, even if the parents do not fully.
- CreateCopyDependency(other.Group, false);
- }
-
- _incompatibleOverlaps.Add(other);
- other.Group._incompatibleOverlaps.Add(new TextureIncompatibleOverlap(this, other.Compatibility));
- }
-
- other.Group.SignalIncompatibleOverlapModified();
- SignalIncompatibleOverlapModified();
- }
-
- /// <summary>
- /// Clear modified flags in the given range.
- /// This will stop any GPU written data from flushing or copying to dependent textures.
- /// </summary>
- /// <param name="range">The range to clear modified flags in</param>
- /// <param name="ignore">Ignore handles that have a copy dependency to the specified group</param>
- public void ClearModified(MultiRange range, TextureGroup ignore = null)
- {
- TextureGroupHandle[] handles = _handles;
-
- foreach (TextureGroupHandle handle in handles)
- {
- // Handles list is not modified by another thread, only replaced, so this is thread safe.
- // Remove modified flags from all overlapping handles, so that the textures don't flush to unmapped/remapped GPU memory.
-
- MultiRange subRange = Storage.Range.Slice((ulong)handle.Offset, (ulong)handle.Size);
-
- if (range.OverlapsWith(subRange))
- {
- if ((ignore == null || !handle.HasDependencyTo(ignore)) && handle.Modified)
- {
- handle.Modified = false;
- Storage.SignalModifiedDirty();
-
- lock (handle.Overlaps)
- {
- foreach (Texture texture in handle.Overlaps)
- {
- texture.SignalModifiedDirty();
- }
- }
- }
- }
- }
-
- Storage.SignalModifiedDirty();
-
- if (_views != null)
- {
- foreach (Texture texture in _views)
- {
- texture.SignalModifiedDirty();
- }
- }
- }
-
- /// <summary>
- /// A flush has been requested on a tracked region. Flush texture data for the given handle.
- /// </summary>
- /// <param name="handle">The handle this flush action is for</param>
- /// <param name="address">The address of the flushing memory access</param>
- /// <param name="size">The size of the flushing memory access</param>
- public void FlushAction(TextureGroupHandle handle, ulong address, ulong size)
- {
- // If the page size is larger than 4KB, we will have a lot of false positives for flushing.
- // Let's avoid flushing textures that are unlikely to be read from CPU to improve performance
- // on those platforms.
- if (!_physicalMemory.Supports4KBPages && !Storage.Info.IsLinear && !_context.IsGpuThread())
- {
- return;
- }
-
- // There is a small gap here where the action is removed but _actionRegistered is still 1.
- // In this case it will skip registering the action, but here we are already handling it,
- // so there shouldn't be any issue as it's the same handler for all actions.
-
- handle.ClearActionRegistered();
-
- if (!handle.Modified)
- {
- return;
- }
-
- bool isGpuThread = _context.IsGpuThread();
-
- if (isGpuThread)
- {
- // No need to wait if we're on the GPU thread, we can just clear the modified flag immediately.
- handle.Modified = false;
- }
-
- _context.Renderer.BackgroundContextAction(() =>
- {
- if (!isGpuThread)
- {
- handle.Sync(_context);
- }
-
- Storage.SignalModifiedDirty();
-
- lock (handle.Overlaps)
- {
- foreach (Texture texture in handle.Overlaps)
- {
- texture.SignalModifiedDirty();
- }
- }
-
- if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities))
- {
- FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, Storage.GetFlushTexture());
- }
- });
- }
-
- /// <summary>
- /// Dispose this texture group, disposing all related memory tracking handles.
- /// </summary>
- public void Dispose()
- {
- foreach (TextureGroupHandle group in _handles)
- {
- group.Dispose();
- }
-
- foreach (TextureIncompatibleOverlap incompatible in _incompatibleOverlaps)
- {
- incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == this);
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs
deleted file mode 100644
index ebb4e9ae..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs
+++ /dev/null
@@ -1,554 +0,0 @@
-using Ryujinx.Cpu.Tracking;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// A tracking handle for a texture group, which represents a range of views in a storage texture.
- /// Retains a list of overlapping texture views, a modified flag, and tracking for each
- /// CPU VA range that the views cover.
- /// Also tracks copy dependencies for the handle - references to other handles that must be kept
- /// in sync with this one before use.
- /// </summary>
- class TextureGroupHandle : IDisposable
- {
- private TextureGroup _group;
- private int _bindCount;
- private int _firstLevel;
- private int _firstLayer;
-
- // Sync state for texture flush.
-
- /// <summary>
- /// The sync number last registered.
- /// </summary>
- private ulong _registeredSync;
-
- /// <summary>
- /// The sync number when the texture was last modified by GPU.
- /// </summary>
- private ulong _modifiedSync;
-
- /// <summary>
- /// Whether a tracking action is currently registered or not. (0/1)
- /// </summary>
- private int _actionRegistered;
-
- /// <summary>
- /// Whether a sync action is currently registered or not.
- /// </summary>
- private bool _syncActionRegistered;
-
- /// <summary>
- /// The byte offset from the start of the storage of this handle.
- /// </summary>
- public int Offset { get; }
-
- /// <summary>
- /// The size in bytes covered by this handle.
- /// </summary>
- public int Size { get; }
-
- /// <summary>
- /// The base slice index for this handle.
- /// </summary>
- public int BaseSlice { get; }
-
- /// <summary>
- /// The number of slices covered by this handle.
- /// </summary>
- public int SliceCount { get; }
-
- /// <summary>
- /// The textures which this handle overlaps with.
- /// </summary>
- public List<Texture> Overlaps { get; }
-
- /// <summary>
- /// The CPU memory tracking handles that cover this handle.
- /// </summary>
- public CpuRegionHandle[] Handles { get; }
-
- /// <summary>
- /// True if a texture overlapping this handle has been modified. Is set false when the flush action is called.
- /// </summary>
- public bool Modified { get; set; }
-
- /// <summary>
- /// Dependencies to handles from other texture groups.
- /// </summary>
- public List<TextureDependency> Dependencies { get; }
-
- /// <summary>
- /// A flag indicating that a copy is required from one of the dependencies.
- /// </summary>
- public bool NeedsCopy => DeferredCopy != null;
-
- /// <summary>
- /// A data copy that must be acknowledged the next time this handle is used.
- /// </summary>
- public TextureGroupHandle DeferredCopy { get; set; }
-
- /// <summary>
- /// Create a new texture group handle, representing a range of views in a storage texture.
- /// </summary>
- /// <param name="group">The TextureGroup that the handle belongs to</param>
- /// <param name="offset">The byte offset from the start of the storage of the handle</param>
- /// <param name="size">The size in bytes covered by the handle</param>
- /// <param name="views">All views of the storage texture, used to calculate overlaps</param>
- /// <param name="firstLayer">The first layer of this handle in the storage texture</param>
- /// <param name="firstLevel">The first level of this handle in the storage texture</param>
- /// <param name="baseSlice">The base slice index of this handle</param>
- /// <param name="sliceCount">The number of slices this handle covers</param>
- /// <param name="handles">The memory tracking handles that cover this handle</param>
- public TextureGroupHandle(TextureGroup group,
- int offset,
- ulong size,
- List<Texture> views,
- int firstLayer,
- int firstLevel,
- int baseSlice,
- int sliceCount,
- CpuRegionHandle[] handles)
- {
- _group = group;
- _firstLayer = firstLayer;
- _firstLevel = firstLevel;
-
- Offset = offset;
- Size = (int)size;
- Overlaps = new List<Texture>();
- Dependencies = new List<TextureDependency>();
-
- BaseSlice = baseSlice;
- SliceCount = sliceCount;
-
- if (views != null)
- {
- RecalculateOverlaps(group, views);
- }
-
- Handles = handles;
- }
-
- /// <summary>
- /// Calculate a list of which views overlap this handle.
- /// </summary>
- /// <param name="group">The parent texture group, used to find a view's base CPU VA offset</param>
- /// <param name="views">The list of views to search for overlaps</param>
- public void RecalculateOverlaps(TextureGroup group, List<Texture> views)
- {
- // Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
- lock (Overlaps)
- {
- int endOffset = Offset + Size;
-
- Overlaps.Clear();
-
- foreach (Texture view in views)
- {
- int viewOffset = group.FindOffset(view);
- if (viewOffset < endOffset && Offset < viewOffset + (int)view.Size)
- {
- Overlaps.Add(view);
- }
- }
- }
- }
-
- /// <summary>
- /// Adds a single texture view as an overlap if its range overlaps.
- /// </summary>
- /// <param name="offset">The offset of the view in the group</param>
- /// <param name="view">The texture to add as an overlap</param>
- public void AddOverlap(int offset, Texture view)
- {
- // Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
-
- if (OverlapsWith(offset, (int)view.Size))
- {
- lock (Overlaps)
- {
- Overlaps.Add(view);
- }
- }
- }
-
- /// <summary>
- /// Removes a single texture view as an overlap if its range overlaps.
- /// </summary>
- /// <param name="offset">The offset of the view in the group</param>
- /// <param name="view">The texture to add as an overlap</param>
- public void RemoveOverlap(int offset, Texture view)
- {
- // Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
-
- if (OverlapsWith(offset, (int)view.Size))
- {
- lock (Overlaps)
- {
- Overlaps.Remove(view);
- }
- }
- }
-
- /// <summary>
- /// Registers a sync action to happen for this handle, and an interim flush action on the tracking handle.
- /// </summary>
- /// <param name="context">The GPU context to register a sync action on</param>
- private void RegisterSync(GpuContext context)
- {
- if (!_syncActionRegistered)
- {
- _modifiedSync = context.SyncNumber;
- context.RegisterSyncAction(SyncAction, true);
- _syncActionRegistered = true;
- }
-
- if (Interlocked.Exchange(ref _actionRegistered, 1) == 0)
- {
- _group.RegisterAction(this);
- }
- }
-
- /// <summary>
- /// Signal that this handle has been modified to any existing dependencies, and set the modified flag.
- /// </summary>
- /// <param name="context">The GPU context to register a sync action on</param>
- public void SignalModified(GpuContext context)
- {
- Modified = true;
-
- // If this handle has any copy dependencies, notify the other handle that a copy needs to be performed.
-
- foreach (TextureDependency dependency in Dependencies)
- {
- dependency.SignalModified();
- }
-
- RegisterSync(context);
- }
-
- /// <summary>
- /// Signal that this handle has either started or ended being modified.
- /// </summary>
- /// <param name="bound">True if this handle is being bound, false if unbound</param>
- /// <param name="context">The GPU context to register a sync action on</param>
- public void SignalModifying(bool bound, GpuContext context)
- {
- SignalModified(context);
-
- // Note: Bind count currently resets to 0 on inherit for safety, as the handle <-> view relationship can change.
- _bindCount = Math.Max(0, _bindCount + (bound ? 1 : -1));
- }
-
- /// <summary>
- /// Synchronize dependent textures, if any of them have deferred a copy from this texture.
- /// </summary>
- public void SynchronizeDependents()
- {
- foreach (TextureDependency dependency in Dependencies)
- {
- TextureGroupHandle otherHandle = dependency.Other.Handle;
-
- if (otherHandle.DeferredCopy == this)
- {
- otherHandle._group.Storage.SynchronizeMemory();
- }
- }
- }
-
- /// <summary>
- /// Wait for the latest sync number that the texture handle was written to,
- /// removing the modified flag if it was reached, or leaving it set if it has not yet been created.
- /// </summary>
- /// <param name="context">The GPU context used to wait for sync</param>
- public void Sync(GpuContext context)
- {
- ulong registeredSync = _registeredSync;
- long diff = (long)(context.SyncNumber - registeredSync);
-
- if (diff > 0)
- {
- context.Renderer.WaitSync(registeredSync);
-
- if ((long)(_modifiedSync - registeredSync) > 0)
- {
- // Flush the data in a previous state. Do not remove the modified flag - it will be removed to ignore following writes.
- return;
- }
-
- Modified = false;
- }
-
- // If the difference is <= 0, no data is not ready yet. Flush any data we can without waiting or removing modified flag.
- }
-
- /// <summary>
- /// Clears the action registered variable, indicating that the tracking action should be
- /// re-registered on the next modification.
- /// </summary>
- public void ClearActionRegistered()
- {
- Interlocked.Exchange(ref _actionRegistered, 0);
- }
-
- /// <summary>
- /// Action to perform when a sync number is registered after modification.
- /// This action will register a read tracking action on the memory tracking handle so that a flush from CPU can happen.
- /// </summary>
- private void SyncAction()
- {
- // The storage will need to signal modified again to update the sync number in future.
- _group.Storage.SignalModifiedDirty();
-
- lock (Overlaps)
- {
- foreach (Texture texture in Overlaps)
- {
- texture.SignalModifiedDirty();
- }
- }
-
- // Register region tracking for CPU? (again)
- _registeredSync = _modifiedSync;
- _syncActionRegistered = false;
-
- if (Interlocked.Exchange(ref _actionRegistered, 1) == 0)
- {
- _group.RegisterAction(this);
- }
- }
-
- /// <summary>
- /// Signal that a copy dependent texture has been modified, and must have its data copied to this one.
- /// </summary>
- /// <param name="copyFrom">The texture handle that must defer a copy to this one</param>
- public void DeferCopy(TextureGroupHandle copyFrom)
- {
- Modified = false;
-
- DeferredCopy = copyFrom;
-
- _group.Storage.SignalGroupDirty();
-
- foreach (Texture overlap in Overlaps)
- {
- overlap.SignalGroupDirty();
- }
- }
-
- /// <summary>
- /// Create a copy dependency between this handle, and another.
- /// </summary>
- /// <param name="other">The handle to create a copy dependency to</param>
- /// <param name="copyToOther">True if a copy should be deferred to all of the other handle's dependencies</param>
- public void CreateCopyDependency(TextureGroupHandle other, bool copyToOther = false)
- {
- // Does this dependency already exist?
- foreach (TextureDependency existing in Dependencies)
- {
- if (existing.Other.Handle == other)
- {
- // Do not need to create it again. May need to set the dirty flag.
- return;
- }
- }
-
- _group.HasCopyDependencies = true;
- other._group.HasCopyDependencies = true;
-
- TextureDependency dependency = new TextureDependency(this);
- TextureDependency otherDependency = new TextureDependency(other);
-
- dependency.Other = otherDependency;
- otherDependency.Other = dependency;
-
- Dependencies.Add(dependency);
- other.Dependencies.Add(otherDependency);
-
- // Recursively create dependency:
- // All of this handle's dependencies must depend on the other.
- foreach (TextureDependency existing in Dependencies.ToArray())
- {
- if (existing != dependency && existing.Other.Handle != other)
- {
- existing.Other.Handle.CreateCopyDependency(other);
- }
- }
-
- // All of the other handle's dependencies must depend on this.
- foreach (TextureDependency existing in other.Dependencies.ToArray())
- {
- if (existing != otherDependency && existing.Other.Handle != this)
- {
- existing.Other.Handle.CreateCopyDependency(this);
-
- if (copyToOther)
- {
- existing.Other.Handle.DeferCopy(this);
- }
- }
- }
- }
-
- /// <summary>
- /// Remove a dependency from this handle's dependency list.
- /// </summary>
- /// <param name="dependency">The dependency to remove</param>
- public void RemoveDependency(TextureDependency dependency)
- {
- Dependencies.Remove(dependency);
- }
-
- /// <summary>
- /// Check if any of this handle's memory tracking handles are dirty.
- /// </summary>
- /// <returns>True if at least one of the handles is dirty</returns>
- private bool CheckDirty()
- {
- return Handles.Any(handle => handle.Dirty);
- }
-
- /// <summary>
- /// Perform a copy from the provided handle to this one, or perform a deferred copy if none is provided.
- /// </summary>
- /// <param name="context">GPU context to register sync for modified handles</param>
- /// <param name="fromHandle">The handle to copy from. If not provided, this method will copy from and clear the deferred copy instead</param>
- /// <returns>True if the copy was performed, false otherwise</returns>
- public bool Copy(GpuContext context, TextureGroupHandle fromHandle = null)
- {
- bool result = false;
- bool shouldCopy = false;
-
- if (fromHandle == null)
- {
- fromHandle = DeferredCopy;
-
- if (fromHandle != null)
- {
- // Only copy if the copy texture is still modified.
- // It will be set as unmodified if new data is written from CPU, as the data previously in the texture will flush.
- // It will also set as unmodified if a copy is deferred to it.
-
- shouldCopy = fromHandle.Modified;
-
- if (fromHandle._bindCount == 0)
- {
- // Repeat the copy in future if the bind count is greater than 0.
- DeferredCopy = null;
- }
- }
- }
- else
- {
- // Copies happen directly when initializing a copy dependency.
- // If dirty, do not copy. Its data no longer matters, and this handle should also be dirty.
- // Also, only direct copy if the data in this handle is not already modified (can be set by copies from modified handles).
- shouldCopy = !fromHandle.CheckDirty() && (fromHandle.Modified || !Modified);
- }
-
- if (shouldCopy)
- {
- Texture from = fromHandle._group.Storage;
- Texture to = _group.Storage;
-
- if (from.ScaleFactor != to.ScaleFactor)
- {
- to.PropagateScale(from);
- }
-
- from.HostTexture.CopyTo(
- to.HostTexture,
- fromHandle._firstLayer,
- _firstLayer,
- fromHandle._firstLevel,
- _firstLevel);
-
- if (fromHandle.Modified)
- {
- Modified = true;
-
- RegisterSync(context);
- }
-
- result = true;
- }
-
- return result;
- }
-
- /// <summary>
- /// Check if this handle has a dependency to a given texture group.
- /// </summary>
- /// <param name="group">The texture group to check for</param>
- /// <returns>True if there is a dependency, false otherwise</returns>
- public bool HasDependencyTo(TextureGroup group)
- {
- foreach (TextureDependency dep in Dependencies)
- {
- if (dep.Other.Handle._group == group)
- {
- return true;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Inherit modified flags and dependencies from another texture handle.
- /// </summary>
- /// <param name="old">The texture handle to inherit from</param>
- /// <param name="withCopies">Whether the handle should inherit copy dependencies or not</param>
- public void Inherit(TextureGroupHandle old, bool withCopies)
- {
- Modified |= old.Modified;
-
- if (withCopies)
- {
- foreach (TextureDependency dependency in old.Dependencies.ToArray())
- {
- CreateCopyDependency(dependency.Other.Handle);
-
- if (dependency.Other.Handle.DeferredCopy == old)
- {
- dependency.Other.Handle.DeferredCopy = this;
- }
- }
-
- DeferredCopy = old.DeferredCopy;
- }
- }
-
- /// <summary>
- /// Check if this region overlaps with another.
- /// </summary>
- /// <param name="address">Base address</param>
- /// <param name="size">Size of the region</param>
- /// <returns>True if overlapping, false otherwise</returns>
- public bool OverlapsWith(int offset, int size)
- {
- return Offset < offset + size && offset < Offset + Size;
- }
-
- /// <summary>
- /// Dispose this texture group handle, removing all its dependencies and disposing its memory tracking handles.
- /// </summary>
- public void Dispose()
- {
- foreach (CpuRegionHandle handle in Handles)
- {
- handle.Dispose();
- }
-
- foreach (TextureDependency dependency in Dependencies.ToArray())
- {
- dependency.Other.Handle.RemoveDependency(dependency.Other);
- }
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
deleted file mode 100644
index a7ee12bc..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
+++ /dev/null
@@ -1,381 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Texture;
-using System;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture information.
- /// </summary>
- readonly struct TextureInfo
- {
- /// <summary>
- /// Address of the texture in GPU mapped memory.
- /// </summary>
- public ulong GpuAddress { get; }
-
- /// <summary>
- /// The width of the texture.
- /// </summary>
- public int Width { get; }
-
- /// <summary>
- /// The height of the texture, or layers count for 1D array textures.
- /// </summary>
- public int Height { get; }
-
- /// <summary>
- /// The depth of the texture (for 3D textures), or layers count for array textures.
- /// </summary>
- public int DepthOrLayers { get; }
-
- /// <summary>
- /// The number of mipmap levels of the texture.
- /// </summary>
- public int Levels { get; }
-
- /// <summary>
- /// The number of samples in the X direction for multisampled textures.
- /// </summary>
- public int SamplesInX { get; }
-
- /// <summary>
- /// The number of samples in the Y direction for multisampled textures.
- /// </summary>
- public int SamplesInY { get; }
-
- /// <summary>
- /// The number of bytes per line for linear textures.
- /// </summary>
- public int Stride { get; }
-
- /// <summary>
- /// Indicates whenever or not the texture is a linear texture.
- /// </summary>
- public bool IsLinear { get; }
-
- /// <summary>
- /// GOB blocks in the Y direction, for block linear textures.
- /// </summary>
- public int GobBlocksInY { get; }
-
- /// <summary>
- /// GOB blocks in the Z direction, for block linear textures.
- /// </summary>
- public int GobBlocksInZ { get; }
-
- /// <summary>
- /// Number of GOB blocks per tile in the X direction, for block linear textures.
- /// </summary>
- public int GobBlocksInTileX { get; }
-
- /// <summary>
- /// Total number of samples for multisampled textures.
- /// </summary>
- public int Samples => SamplesInX * SamplesInY;
-
- /// <summary>
- /// Texture target type.
- /// </summary>
- public Target Target { get; }
-
- /// <summary>
- /// Texture format information.
- /// </summary>
- public FormatInfo FormatInfo { get; }
-
- /// <summary>
- /// Depth-stencil mode of the texture. This defines whenever the depth or stencil value is read from shaders,
- /// for depth-stencil texture formats.
- /// </summary>
- public DepthStencilMode DepthStencilMode { get; }
-
- /// <summary>
- /// Texture swizzle for the red color channel.
- /// </summary>
- public SwizzleComponent SwizzleR { get; }
-
- /// <summary>
- /// Texture swizzle for the green color channel.
- /// </summary>
- public SwizzleComponent SwizzleG { get; }
-
- /// <summary>
- /// Texture swizzle for the blue color channel.
- /// </summary>
- public SwizzleComponent SwizzleB { get; }
-
- /// <summary>
- /// Texture swizzle for the alpha color channel.
- /// </summary>
- public SwizzleComponent SwizzleA { get; }
-
- /// <summary>
- /// Constructs the texture information structure.
- /// </summary>
- /// <param name="gpuAddress">The GPU address of the texture</param>
- /// <param name="width">The width of the texture</param>
- /// <param name="height">The height or the texture</param>
- /// <param name="depthOrLayers">The depth or layers count of the texture</param>
- /// <param name="levels">The amount of mipmap levels of the texture</param>
- /// <param name="samplesInX">The number of samples in the X direction for multisample textures, should be 1 otherwise</param>
- /// <param name="samplesInY">The number of samples in the Y direction for multisample textures, should be 1 otherwise</param>
- /// <param name="stride">The stride for linear textures</param>
- /// <param name="isLinear">Whenever the texture is linear or block linear</param>
- /// <param name="gobBlocksInY">Number of GOB blocks in the Y direction</param>
- /// <param name="gobBlocksInZ">Number of GOB blocks in the Z direction</param>
- /// <param name="gobBlocksInTileX">Number of GOB blocks per tile in the X direction</param>
- /// <param name="target">Texture target type</param>
- /// <param name="formatInfo">Texture format information</param>
- /// <param name="depthStencilMode">Depth-stencil mode</param>
- /// <param name="swizzleR">Swizzle for the red color channel</param>
- /// <param name="swizzleG">Swizzle for the green color channel</param>
- /// <param name="swizzleB">Swizzle for the blue color channel</param>
- /// <param name="swizzleA">Swizzle for the alpha color channel</param>
- public TextureInfo(
- ulong gpuAddress,
- 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)
- {
- GpuAddress = gpuAddress;
- 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;
- }
-
- /// <summary>
- /// Gets the real texture depth.
- /// Returns 1 for any target other than 3D textures.
- /// </summary>
- /// <returns>Texture depth</returns>
- public int GetDepth()
- {
- return GetDepth(Target, DepthOrLayers);
- }
-
- /// <summary>
- /// Gets the real texture depth.
- /// Returns 1 for any target other than 3D textures.
- /// </summary>
- /// <param name="target">Texture target</param>
- /// <param name="depthOrLayers">Texture depth if the texture is 3D, otherwise ignored</param>
- /// <returns>Texture depth</returns>
- public static int GetDepth(Target target, int depthOrLayers)
- {
- return target == Target.Texture3D ? depthOrLayers : 1;
- }
-
- /// <summary>
- /// Gets the number of layers of the texture.
- /// Returns 1 for non-array textures, 6 for cubemap textures, and layer faces for cubemap array textures.
- /// </summary>
- /// <returns>The number of texture layers</returns>
- public int GetLayers()
- {
- return GetLayers(Target, DepthOrLayers);
- }
-
- /// <summary>
- /// Gets the number of layers of the texture.
- /// Returns 1 for non-array textures, 6 for cubemap textures, and layer faces for cubemap array textures.
- /// </summary>
- /// <param name="target">Texture target</param>
- /// <param name="depthOrLayers">Texture layers if the is a array texture, ignored otherwise</param>
- /// <returns>The number of texture layers</returns>
- public static int GetLayers(Target target, int depthOrLayers)
- {
- 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;
- }
- }
-
- /// <summary>
- /// Gets the number of 2D slices of the texture.
- /// Returns 6 for cubemap textures, layer faces for cubemap array textures, and DepthOrLayers for everything else.
- /// </summary>
- /// <returns>The number of texture slices</returns>
- public int GetSlices()
- {
- if (Target == Target.Texture3D || 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;
- }
- }
-
- /// <summary>
- /// Calculates the size information from the texture information.
- /// </summary>
- /// <param name="layerSize">Optional size of each texture layer in bytes</param>
- /// <returns>Texture size information</returns>
- public SizeInfo CalculateSizeInfo(int layerSize = 0)
- {
- if (Target == Target.TextureBuffer)
- {
- return new SizeInfo(Width * FormatInfo.BytesPerPixel);
- }
- else if (IsLinear)
- {
- return SizeCalculator.GetLinearTextureSize(
- Stride,
- Height,
- FormatInfo.BlockHeight);
- }
- else
- {
- return SizeCalculator.GetBlockLinearTextureSize(
- Width,
- Height,
- GetDepth(),
- Levels,
- GetLayers(),
- FormatInfo.BlockWidth,
- FormatInfo.BlockHeight,
- FormatInfo.BytesPerPixel,
- GobBlocksInY,
- GobBlocksInZ,
- GobBlocksInTileX,
- layerSize);
- }
- }
-
- /// <summary>
- /// Creates texture information for a given mipmap level of the specified parent texture and this information.
- /// </summary>
- /// <param name="parent">The parent texture</param>
- /// <param name="firstLevel">The first level of the texture view</param>
- /// <returns>The adjusted texture information with the new size</returns>
- public TextureInfo CreateInfoForLevelView(Texture parent, 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 && !FormatInfo.IsCompressed)
- {
- width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
- height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
- }
- else if (!parent.Info.FormatInfo.IsCompressed && FormatInfo.IsCompressed)
- {
- width *= FormatInfo.BlockWidth;
- height *= FormatInfo.BlockHeight;
- }
-
- int depthOrLayers;
-
- if (Target == Target.Texture3D)
- {
- depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
- }
- else
- {
- depthOrLayers = DepthOrLayers;
- }
-
- // 2D and 2D multisample textures are not considered compatible.
- // This specific case is required for copies, where the source texture might be multisample.
- // In this case, we inherit the parent texture multisample state.
- Target target = Target;
- int samplesInX = SamplesInX;
- int samplesInY = SamplesInY;
-
- if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample)
- {
- target = Target.Texture2DMultisample;
- samplesInX = parent.Info.SamplesInX;
- samplesInY = parent.Info.SamplesInY;
- }
-
- return new TextureInfo(
- GpuAddress,
- width,
- height,
- depthOrLayers,
- Levels,
- samplesInX,
- samplesInY,
- Stride,
- IsLinear,
- GobBlocksInY,
- GobBlocksInZ,
- GobBlocksInTileX,
- target,
- FormatInfo,
- DepthStencilMode,
- SwizzleR,
- SwizzleG,
- SwizzleB,
- SwizzleA);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
deleted file mode 100644
index 266f6285..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ /dev/null
@@ -1,498 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Engine.Types;
-using Ryujinx.Graphics.Gpu.Shader;
-using System;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture manager.
- /// </summary>
- class TextureManager : IDisposable
- {
- private readonly GpuContext _context;
- private readonly GpuChannel _channel;
-
- private readonly TextureBindingsManager _cpBindingsManager;
- private readonly TextureBindingsManager _gpBindingsManager;
- private readonly TexturePoolCache _texturePoolCache;
- private readonly SamplerPoolCache _samplerPoolCache;
-
- private readonly Texture[] _rtColors;
- private readonly ITexture[] _rtHostColors;
- private Texture _rtDepthStencil;
- private ITexture _rtHostDs;
-
- public int ClipRegionWidth { get; private set; }
- public int ClipRegionHeight { get; private set; }
-
- /// <summary>
- /// The scaling factor applied to all currently bound render targets.
- /// </summary>
- public float RenderTargetScale { get; private set; } = 1f;
-
- /// <summary>
- /// Creates a new instance of the texture manager.
- /// </summary>
- /// <param name="context">GPU context that the texture manager belongs to</param>
- /// <param name="channel">GPU channel that the texture manager belongs to</param>
- public TextureManager(GpuContext context, GpuChannel channel)
- {
- _context = context;
- _channel = channel;
-
- TexturePoolCache texturePoolCache = new TexturePoolCache(context);
- SamplerPoolCache samplerPoolCache = new SamplerPoolCache(context);
-
- float[] scales = new float[64];
- new Span<float>(scales).Fill(1f);
-
- _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: true);
- _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: false);
- _texturePoolCache = texturePoolCache;
- _samplerPoolCache = samplerPoolCache;
-
- _rtColors = new Texture[Constants.TotalRenderTargets];
- _rtHostColors = new ITexture[Constants.TotalRenderTargets];
- }
-
- /// <summary>
- /// Sets the texture and image bindings for the compute pipeline.
- /// </summary>
- /// <param name="bindings">Bindings for the active shader</param>
- public void SetComputeBindings(CachedShaderBindings bindings)
- {
- _cpBindingsManager.SetBindings(bindings);
- }
-
- /// <summary>
- /// Sets the texture and image bindings for the graphics pipeline.
- /// </summary>
- /// <param name="bindings">Bindings for the active shader</param>
- public void SetGraphicsBindings(CachedShaderBindings bindings)
- {
- _gpBindingsManager.SetBindings(bindings);
- }
-
- /// <summary>
- /// Sets the texture constant buffer index on the compute pipeline.
- /// </summary>
- /// <param name="index">The texture constant buffer index</param>
- public void SetComputeTextureBufferIndex(int index)
- {
- _cpBindingsManager.SetTextureBufferIndex(index);
- }
-
- /// <summary>
- /// Sets the texture constant buffer index on the graphics pipeline.
- /// </summary>
- /// <param name="index">The texture constant buffer index</param>
- public void SetGraphicsTextureBufferIndex(int index)
- {
- _gpBindingsManager.SetTextureBufferIndex(index);
- }
-
- /// <summary>
- /// Sets the current sampler pool on the compute pipeline.
- /// </summary>
- /// <param name="gpuVa">The start GPU virtual address of the sampler pool</param>
- /// <param name="maximumId">The maximum ID of the sampler pool</param>
- /// <param name="samplerIndex">The indexing type of the sampler pool</param>
- public void SetComputeSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
- {
- _cpBindingsManager.SetSamplerPool(gpuVa, maximumId, samplerIndex);
- }
-
- /// <summary>
- /// Sets the current sampler pool on the graphics pipeline.
- /// </summary>
- /// <param name="gpuVa">The start GPU virtual address of the sampler pool</param>
- /// <param name="maximumId">The maximum ID of the sampler pool</param>
- /// <param name="samplerIndex">The indexing type of the sampler pool</param>
- public void SetGraphicsSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
- {
- _gpBindingsManager.SetSamplerPool(gpuVa, maximumId, samplerIndex);
- }
-
- /// <summary>
- /// Sets the current texture pool on the compute pipeline.
- /// </summary>
- /// <param name="gpuVa">The start GPU virtual address of the texture pool</param>
- /// <param name="maximumId">The maximum ID of the texture pool</param>
- public void SetComputeTexturePool(ulong gpuVa, int maximumId)
- {
- _cpBindingsManager.SetTexturePool(gpuVa, maximumId);
- }
-
- /// <summary>
- /// Sets the current texture pool on the graphics pipeline.
- /// </summary>
- /// <param name="gpuVa">The start GPU virtual address of the texture pool</param>
- /// <param name="maximumId">The maximum ID of the texture pool</param>
- public void SetGraphicsTexturePool(ulong gpuVa, int maximumId)
- {
- _gpBindingsManager.SetTexturePool(gpuVa, maximumId);
- }
-
- /// <summary>
- /// Check if a texture's scale must be updated to match the configured resolution scale.
- /// </summary>
- /// <param name="texture">The texture to check</param>
- /// <returns>True if the scale needs updating, false if the scale is up to date</returns>
- private bool ScaleNeedsUpdated(Texture texture)
- {
- return texture != null && !(texture.ScaleMode == TextureScaleMode.Blacklisted || texture.ScaleMode == TextureScaleMode.Undesired) && texture.ScaleFactor != GraphicsConfig.ResScale;
- }
-
- /// <summary>
- /// Sets the render target color buffer.
- /// </summary>
- /// <param name="index">The index of the color buffer to set (up to 8)</param>
- /// <param name="color">The color buffer texture</param>
- /// <returns>True if render target scale must be updated.</returns>
- public bool SetRenderTargetColor(int index, Texture color)
- {
- bool hasValue = color != null;
- bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor);
-
- if (_rtColors[index] != color)
- {
- _rtColors[index]?.SignalModifying(false);
-
- if (color != null)
- {
- color.SynchronizeMemory();
- color.SignalModifying(true);
- }
-
- _rtColors[index] = color;
- }
-
- return changesScale || ScaleNeedsUpdated(color);
- }
-
- /// <summary>
- /// Sets the render target depth-stencil buffer.
- /// </summary>
- /// <param name="depthStencil">The depth-stencil buffer texture</param>
- /// <returns>True if render target scale must be updated.</returns>
- public bool SetRenderTargetDepthStencil(Texture depthStencil)
- {
- bool hasValue = depthStencil != null;
- bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor);
-
- if (_rtDepthStencil != depthStencil)
- {
- _rtDepthStencil?.SignalModifying(false);
-
- if (depthStencil != null)
- {
- depthStencil.SynchronizeMemory();
- depthStencil.SignalModifying(true);
- }
-
- _rtDepthStencil = depthStencil;
- }
-
- return changesScale || ScaleNeedsUpdated(depthStencil);
- }
-
- /// <summary>
- /// Sets the host clip region, which should be the intersection of all render target texture sizes.
- /// </summary>
- /// <param name="width">Width of the clip region, defined as the minimum width across all bound textures</param>
- /// <param name="height">Height of the clip region, defined as the minimum height across all bound textures</param>
- public void SetClipRegion(int width, int height)
- {
- ClipRegionWidth = width;
- ClipRegionHeight = height;
- }
-
- /// <summary>
- /// Gets the first available bound colour target, or the depth stencil target if not present.
- /// </summary>
- /// <returns>The first bound colour target, otherwise the depth stencil target</returns>
- public Texture GetAnyRenderTarget()
- {
- return _rtColors[0] ?? _rtDepthStencil;
- }
-
- /// <summary>
- /// Updates the Render Target scale, given the currently bound render targets.
- /// This will update scale to match the configured scale, scale textures that are eligible but not scaled,
- /// and propagate blacklisted status from one texture to the ones bound with it.
- /// </summary>
- /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
- public void UpdateRenderTargetScale(int singleUse)
- {
- // Make sure all scales for render targets are at the highest they should be. Blacklisted targets should propagate their scale to the other targets.
- bool mismatch = false;
- bool blacklisted = false;
- bool hasUpscaled = false;
- bool hasUndesired = false;
- float targetScale = GraphicsConfig.ResScale;
-
- void ConsiderTarget(Texture target)
- {
- if (target == null) return;
- float scale = target.ScaleFactor;
-
- switch (target.ScaleMode)
- {
- case TextureScaleMode.Blacklisted:
- mismatch |= scale != 1f;
- blacklisted = true;
- break;
- case TextureScaleMode.Eligible:
- mismatch = true; // We must make a decision.
- break;
- case TextureScaleMode.Undesired:
- hasUndesired = true;
- mismatch |= scale != 1f || hasUpscaled; // If another target is upscaled, scale this one up too.
- break;
- case TextureScaleMode.Scaled:
- hasUpscaled = true;
- mismatch |= hasUndesired || scale != targetScale; // If the target scale has changed, reset the scale for all targets.
- break;
- }
- }
-
- if (singleUse != -1)
- {
- // If only one target is in use (by a clear, for example) the others do not need to be checked for mismatching scale.
- ConsiderTarget(_rtColors[singleUse]);
- }
- else
- {
- foreach (Texture color in _rtColors)
- {
- ConsiderTarget(color);
- }
- }
-
- ConsiderTarget(_rtDepthStencil);
-
- mismatch |= blacklisted && hasUpscaled;
-
- if (blacklisted || (hasUndesired && !hasUpscaled))
- {
- targetScale = 1f;
- }
-
- if (mismatch)
- {
- if (blacklisted)
- {
- // Propagate the blacklisted state to the other textures.
- foreach (Texture color in _rtColors)
- {
- color?.BlacklistScale();
- }
-
- _rtDepthStencil?.BlacklistScale();
- }
- else
- {
- // Set the scale of the other textures.
- foreach (Texture color in _rtColors)
- {
- color?.SetScale(targetScale);
- }
-
- _rtDepthStencil?.SetScale(targetScale);
- }
- }
-
- RenderTargetScale = targetScale;
- }
-
- /// <summary>
- /// Gets a texture and a sampler from their respective pools from a texture ID and a sampler ID.
- /// </summary>
- /// <param name="textureId">ID of the texture</param>
- /// <param name="samplerId">ID of the sampler</param>
- public (Texture, Sampler) GetGraphicsTextureAndSampler(int textureId, int samplerId)
- {
- return _gpBindingsManager.GetTextureAndSampler(textureId, samplerId);
- }
-
- /// <summary>
- /// Commits bindings on the compute pipeline.
- /// </summary>
- /// <param name="specState">Specialization state for the bound shader</param>
- /// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
- public bool CommitComputeBindings(ShaderSpecializationState specState)
- {
- // Every time we switch between graphics and compute work,
- // we must rebind everything.
- // Since compute work happens less often, we always do that
- // before and after the compute dispatch.
-
- _texturePoolCache.Tick();
- _samplerPoolCache.Tick();
-
- _cpBindingsManager.Rebind();
- bool result = _cpBindingsManager.CommitBindings(specState);
- _gpBindingsManager.Rebind();
-
- return result;
- }
-
- /// <summary>
- /// Commits bindings on the graphics pipeline.
- /// </summary>
- /// <param name="specState">Specialization state for the bound shader</param>
- /// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
- public bool CommitGraphicsBindings(ShaderSpecializationState specState)
- {
- _texturePoolCache.Tick();
- _samplerPoolCache.Tick();
-
- bool result = _gpBindingsManager.CommitBindings(specState);
-
- UpdateRenderTargets();
-
- return result;
- }
-
- /// <summary>
- /// Returns a texture pool from the cache, with the given address and maximum id.
- /// </summary>
- /// <param name="poolGpuVa">GPU virtual address of the texture pool</param>
- /// <param name="maximumId">Maximum ID of the texture pool</param>
- /// <returns>The texture pool</returns>
- public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
- {
- ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
-
- TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId);
-
- return texturePool;
- }
-
- /// <summary>
- /// Gets a texture descriptor used on the compute pipeline.
- /// </summary>
- /// <param name="poolGpuVa">GPU virtual address of the texture pool</param>
- /// <param name="bufferIndex">Index of the constant buffer with texture handles</param>
- /// <param name="maximumId">Maximum ID of the texture pool</param>
- /// <param name="handle">Shader "fake" handle of the texture</param>
- /// <param name="cbufSlot">Shader constant buffer slot of the texture</param>
- /// <returns>The texture descriptor</returns>
- public TextureDescriptor GetComputeTextureDescriptor(ulong poolGpuVa, int bufferIndex, int maximumId, int handle, int cbufSlot)
- {
- return _cpBindingsManager.GetTextureDescriptor(poolGpuVa, bufferIndex, maximumId, 0, handle, cbufSlot);
- }
-
- /// <summary>
- /// Gets a texture descriptor used on the graphics pipeline.
- /// </summary>
- /// <param name="poolGpuVa">GPU virtual address of the texture pool</param>
- /// <param name="bufferIndex">Index of the constant buffer with texture handles</param>
- /// <param name="maximumId">Maximum ID of the texture pool</param>
- /// <param name="stageIndex">Index of the shader stage where the texture is bound</param>
- /// <param name="handle">Shader "fake" handle of the texture</param>
- /// <param name="cbufSlot">Shader constant buffer slot of the texture</param>
- /// <returns>The texture descriptor</returns>
- public TextureDescriptor GetGraphicsTextureDescriptor(
- ulong poolGpuVa,
- int bufferIndex,
- int maximumId,
- int stageIndex,
- int handle,
- int cbufSlot)
- {
- return _gpBindingsManager.GetTextureDescriptor(poolGpuVa, bufferIndex, maximumId, stageIndex, handle, cbufSlot);
- }
-
- /// <summary>
- /// Update host framebuffer attachments based on currently bound render target buffers.
- /// </summary>
- public void UpdateRenderTargets()
- {
- bool anyChanged = false;
-
- if (_rtHostDs != _rtDepthStencil?.HostTexture)
- {
- _rtHostDs = _rtDepthStencil?.HostTexture;
-
- anyChanged = true;
- }
-
- 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.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
- }
- }
-
- /// <summary>
- /// Update host framebuffer attachments based on currently bound render target buffers.
- /// </summary>
- /// <remarks>
- /// All color attachments will be unbound.
- /// </remarks>
- public void UpdateRenderTargetDepthStencil()
- {
- new Span<ITexture>(_rtHostColors).Fill(null);
- _rtHostDs = _rtDepthStencil?.HostTexture;
-
- _context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
- }
-
- /// <summary>
- /// Forces the texture and sampler pools to be re-loaded from the cache on next use.
- /// </summary>
- public void ReloadPools()
- {
- _cpBindingsManager.ReloadPools();
- _gpBindingsManager.ReloadPools();
- }
-
- /// <summary>
- /// Forces all textures, samplers, images and render targets to be rebound the next time
- /// CommitGraphicsBindings is called.
- /// </summary>
- public void Rebind()
- {
- _gpBindingsManager.Rebind();
-
- for (int index = 0; index < _rtHostColors.Length; index++)
- {
- _rtHostColors[index] = null;
- }
-
- _rtHostDs = null;
- }
-
- /// <summary>
- /// Disposes the texture manager.
- /// It's an error to use the texture manager after disposal.
- /// </summary>
- public void Dispose()
- {
- // Textures are owned by the texture cache, so we shouldn't dispose the texture pool cache.
- _samplerPoolCache.Dispose();
-
- for (int i = 0; i < _rtColors.Length; i++)
- {
- _rtColors[i]?.DecrementReferenceCount();
- _rtColors[i] = null;
- }
-
- _rtDepthStencil?.DecrementReferenceCount();
- _rtDepthStencil = null;
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs b/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs
deleted file mode 100644
index 1351bf24..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureMatchQuality.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- enum TextureMatchQuality
- {
- NoMatch,
- FormatAlias,
- Perfect
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs b/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
deleted file mode 100644
index 0461888f..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Multisampled texture samples count.
- /// </summary>
- enum TextureMsaaMode
- {
- Ms1x1 = 0,
- Ms2x2 = 2,
- Ms4x2 = 4,
- Ms2x1 = 5,
- Ms4x4 = 6
- }
-
- static class TextureMsaaModeConverter
- {
- /// <summary>
- /// Returns the total number of samples from the MSAA mode.
- /// </summary>
- /// <param name="msaaMode">The MSAA mode</param>
- /// <returns>The total number of samples</returns>
- public static int SamplesCount(this TextureMsaaMode msaaMode)
- {
- return msaaMode switch
- {
- TextureMsaaMode.Ms2x1 => 2,
- TextureMsaaMode.Ms2x2 => 4,
- TextureMsaaMode.Ms4x2 => 8,
- TextureMsaaMode.Ms4x4 => 16,
- _ => 1
- };
- }
-
- /// <summary>
- /// Returns the number of samples in the X direction from the MSAA mode.
- /// </summary>
- /// <param name="msaaMode">The MSAA mode</param>
- /// <returns>The number of samples in the X direction</returns>
- public static int SamplesInX(this TextureMsaaMode msaaMode)
- {
- return msaaMode switch
- {
- TextureMsaaMode.Ms2x1 => 2,
- TextureMsaaMode.Ms2x2 => 2,
- TextureMsaaMode.Ms4x2 => 4,
- TextureMsaaMode.Ms4x4 => 4,
- _ => 1
- };
- }
-
- /// <summary>
- /// Returns the number of samples in the Y direction from the MSAA mode.
- /// </summary>
- /// <param name="msaaMode">The MSAA mode</param>
- /// <returns>The number of samples in the Y direction</returns>
- public static int SamplesInY(this TextureMsaaMode msaaMode)
- {
- return msaaMode switch
- {
- TextureMsaaMode.Ms2x1 => 1,
- TextureMsaaMode.Ms2x2 => 2,
- TextureMsaaMode.Ms4x2 => 2,
- TextureMsaaMode.Ms4x4 => 4,
- _ => 1
- };
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
deleted file mode 100644
index 5277e789..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ /dev/null
@@ -1,603 +0,0 @@
-using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Texture;
-using Ryujinx.Memory.Range;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture pool.
- /// </summary>
- class TexturePool : Pool<Texture, TextureDescriptor>, IPool<TexturePool>
- {
- /// <summary>
- /// A request to dereference a texture from a pool.
- /// </summary>
- private struct DereferenceRequest
- {
- /// <summary>
- /// Whether the dereference is due to a mapping change or not.
- /// </summary>
- public readonly bool IsRemapped;
-
- /// <summary>
- /// The texture being dereferenced.
- /// </summary>
- public readonly Texture Texture;
-
- /// <summary>
- /// The ID of the pool entry this reference belonged to.
- /// </summary>
- public readonly int ID;
-
- /// <summary>
- /// Create a dereference request for a texture with a specific pool ID, and remapped flag.
- /// </summary>
- /// <param name="isRemapped">Whether the dereference is due to a mapping change or not</param>
- /// <param name="texture">The texture being dereferenced</param>
- /// <param name="id">The ID of the pool entry, used to restore remapped textures</param>
- private DereferenceRequest(bool isRemapped, Texture texture, int id)
- {
- IsRemapped = isRemapped;
- Texture = texture;
- ID = id;
- }
-
- /// <summary>
- /// Create a dereference request for a texture removal.
- /// </summary>
- /// <param name="texture">The texture being removed</param>
- /// <returns>A texture removal dereference request</returns>
- public static DereferenceRequest Remove(Texture texture)
- {
- return new DereferenceRequest(false, texture, 0);
- }
-
- /// <summary>
- /// Create a dereference request for a texture remapping with a specific pool ID.
- /// </summary>
- /// <param name="texture">The texture being remapped</param>
- /// <param name="id">The ID of the pool entry, used to restore remapped textures</param>
- /// <returns>A remap dereference request</returns>
- public static DereferenceRequest Remap(Texture texture, int id)
- {
- return new DereferenceRequest(true, texture, id);
- }
- }
-
- private readonly GpuChannel _channel;
- private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new ConcurrentQueue<DereferenceRequest>();
- private TextureDescriptor _defaultDescriptor;
-
- /// <summary>
- /// Linked list node used on the texture pool cache.
- /// </summary>
- public LinkedListNode<TexturePool> CacheNode { get; set; }
-
- /// <summary>
- /// Timestamp used by the texture pool cache, updated on every use of this texture pool.
- /// </summary>
- public ulong CacheTimestamp { get; set; }
-
- /// <summary>
- /// Creates a new instance of the texture pool.
- /// </summary>
- /// <param name="context">GPU context that the texture pool belongs to</param>
- /// <param name="channel">GPU channel that the texture pool belongs to</param>
- /// <param name="address">Address of the texture pool in guest memory</param>
- /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
- public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
- {
- _channel = channel;
- }
-
- /// <summary>
- /// Gets the texture descripor and texture with the given ID with no bounds check or synchronization.
- /// </summary>
- /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
- /// <param name="texture">The texture with the given ID</param>
- /// <returns>The texture descriptor with the given ID</returns>
- private ref readonly TextureDescriptor GetInternal(int id, out Texture texture)
- {
- texture = Items[id];
-
- ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(id);
-
- if (texture == null)
- {
- texture = PhysicalMemory.TextureCache.FindShortCache(descriptor);
-
- if (texture == null)
- {
- TextureInfo info = GetInfo(descriptor, out int layerSize);
-
- // The dereference queue can put our texture back on the cache.
- if ((texture = ProcessDereferenceQueue(id)) != null)
- {
- return ref descriptor;
- }
-
- texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
-
- // If this happens, then the texture address is invalid, we can't add it to the cache.
- if (texture == null)
- {
- return ref descriptor;
- }
- }
- else
- {
- texture.SynchronizeMemory();
- }
-
- Items[id] = texture;
-
- texture.IncrementReferenceCount(this, id, descriptor.UnpackAddress());
-
- DescriptorCache[id] = descriptor;
- }
- else
- {
- // On the path above (texture not yet in the pool), memory is automatically synchronized on texture creation.
- texture.SynchronizeMemory();
- }
-
- return ref descriptor;
- }
-
- /// <summary>
- /// Gets the texture with the given ID.
- /// </summary>
- /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
- /// <returns>The texture with the given ID</returns>
- public override Texture Get(int id)
- {
- if ((uint)id >= Items.Length)
- {
- return null;
- }
-
- if (SequenceNumber != Context.SequenceNumber)
- {
- SequenceNumber = Context.SequenceNumber;
-
- SynchronizeMemory();
- }
-
- GetInternal(id, out Texture texture);
-
- return texture;
- }
-
- /// <summary>
- /// Gets the texture descriptor and texture with the given ID.
- /// </summary>
- /// <remarks>
- /// This method assumes that the pool has been manually synchronized before doing binding.
- /// </remarks>
- /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
- /// <param name="texture">The texture with the given ID</param>
- /// <returns>The texture descriptor with the given ID</returns>
- public ref readonly TextureDescriptor GetForBinding(int id, out Texture texture)
- {
- if ((uint)id >= Items.Length)
- {
- texture = null;
- return ref _defaultDescriptor;
- }
-
- // When getting for binding, assume the pool has already been synchronized.
-
- return ref GetInternal(id, out texture);
- }
-
- /// <summary>
- /// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
- /// </summary>
- /// <returns>A number that increments each time a modification is detected</returns>
- public int CheckModified()
- {
- if (SequenceNumber != Context.SequenceNumber)
- {
- SequenceNumber = Context.SequenceNumber;
-
- SynchronizeMemory();
- }
-
- return ModifiedSequenceNumber;
- }
-
- /// <summary>
- /// Forcibly remove a texture from this pool's items.
- /// If deferred, the dereference will be queued to occur on the render thread.
- /// </summary>
- /// <param name="texture">The texture being removed</param>
- /// <param name="id">The ID of the texture in this pool</param>
- /// <param name="deferred">If true, queue the dereference to happen on the render thread, otherwise dereference immediately</param>
- public void ForceRemove(Texture texture, int id, bool deferred)
- {
- var previous = Interlocked.Exchange(ref Items[id], null);
-
- if (deferred)
- {
- if (previous != null)
- {
- _dereferenceQueue.Enqueue(DereferenceRequest.Remove(texture));
- }
- }
- else
- {
- texture.DecrementReferenceCount();
- }
- }
-
- /// <summary>
- /// Queues a request to update a texture's mapping.
- /// Mapping is updated later to avoid deleting the texture if it is still sparsely mapped.
- /// </summary>
- /// <param name="texture">Texture with potential mapping change</param>
- /// <param name="id">ID in cache of texture with potential mapping change</param>
- public void QueueUpdateMapping(Texture texture, int id)
- {
- if (Interlocked.Exchange(ref Items[id], null) == texture)
- {
- _dereferenceQueue.Enqueue(DereferenceRequest.Remap(texture, id));
- }
- }
-
- /// <summary>
- /// Process the dereference queue, decrementing the reference count for each texture in it.
- /// This is used to ensure that texture disposal happens on the render thread.
- /// </summary>
- /// <param name="id">The ID of the entry that triggered this method</param>
- /// <returns>Texture that matches the entry ID if it has been readded to the cache.</returns>
- private Texture ProcessDereferenceQueue(int id = -1)
- {
- while (_dereferenceQueue.TryDequeue(out DereferenceRequest request))
- {
- Texture texture = request.Texture;
-
- // Unmapped storage textures can swap their ranges. The texture must be storage with no views or dependencies.
- // TODO: Would need to update ranges on views, or guarantee that ones where the range changes can be instantly deleted.
-
- if (request.IsRemapped && texture.Group.Storage == texture && !texture.HasViews && !texture.Group.HasCopyDependencies)
- {
- // Has the mapping for this texture changed?
- ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(request.ID);
-
- ulong address = descriptor.UnpackAddress();
-
- MultiRange range = _channel.MemoryManager.GetPhysicalRegions(address, texture.Size);
-
- // If the texture is not mapped at all, delete its reference.
-
- if (range.Count == 1 && range.GetSubRange(0).Address == MemoryManager.PteUnmapped)
- {
- texture.DecrementReferenceCount();
- continue;
- }
-
- Items[request.ID] = texture;
-
- // Create a new pool reference, as the last one was removed on unmap.
-
- texture.IncrementReferenceCount(this, request.ID, address);
- texture.DecrementReferenceCount();
-
- // Refetch the range. Changes since the last check could have been lost
- // as the cache entry was not restored (required to queue mapping change).
-
- range = _channel.MemoryManager.GetPhysicalRegions(address, texture.Size);
-
- if (!range.Equals(texture.Range))
- {
- // Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
- if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range))
- {
- // Texture could not be remapped due to a collision, just delete it.
- if (Interlocked.Exchange(ref Items[request.ID], null) != null)
- {
- // If this is null, a request was already queued to decrement reference.
- texture.DecrementReferenceCount(this, request.ID);
- }
- continue;
- }
- }
-
- if (request.ID == id)
- {
- return texture;
- }
- }
- else
- {
- texture.DecrementReferenceCount();
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Implementation of the texture pool range invalidation.
- /// </summary>
- /// <param name="address">Start address of the range of the texture pool</param>
- /// <param name="size">Size of the range being invalidated</param>
- protected override void InvalidateRangeImpl(ulong address, ulong size)
- {
- ProcessDereferenceQueue();
-
- ulong endAddress = address + size;
-
- for (; address < endAddress; address += DescriptorSize)
- {
- int id = (int)((address - Address) / DescriptorSize);
-
- Texture texture = Items[id];
-
- if (texture != null)
- {
- ref TextureDescriptor cachedDescriptor = ref DescriptorCache[id];
- ref readonly TextureDescriptor descriptor = ref GetDescriptorRefAddress(address);
-
- // 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 (descriptor.Equals(ref cachedDescriptor))
- {
- continue;
- }
-
- if (texture.HasOneReference())
- {
- _channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
- }
-
- if (Interlocked.Exchange(ref Items[id], null) != null)
- {
- texture.DecrementReferenceCount(this, id);
- }
- }
- }
- }
-
- /// <summary>
- /// Gets texture information from a texture descriptor.
- /// </summary>
- /// <param name="descriptor">The texture descriptor</param>
- /// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param>
- /// <returns>The texture information</returns>
- private TextureInfo GetInfo(in TextureDescriptor descriptor, out int layerSize)
- {
- 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);
-
- int width = target == Target.TextureBuffer ? descriptor.UnpackBufferTextureWidth() : descriptor.UnpackWidth();
- int height = descriptor.UnpackHeight();
-
- if (target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray)
- {
- // This is divided back before the backend texture is created.
- width *= samplesInX;
- height *= samplesInY;
- }
-
- // We use 2D targets for 1D textures as that makes texture cache
- // management easier. We don't know the target for render target
- // and copies, so those would normally use 2D targets, which are
- // not compatible with 1D targets. By doing that we also allow those
- // to match when looking for compatible textures on the cache.
- if (target == Target.Texture1D)
- {
- target = Target.Texture2D;
- height = 1;
- }
- else if (target == Target.Texture1DArray)
- {
- target = Target.Texture2DArray;
- height = 1;
- }
-
- uint format = descriptor.UnpackFormat();
- bool srgb = descriptor.UnpackSrgb();
-
- ulong gpuVa = descriptor.UnpackAddress();
-
- if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
- {
- if (gpuVa != 0 && (int)format > 0)
- {
- Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
- }
-
- formatInfo = FormatInfo.Default;
- }
-
- int gobBlocksInY = descriptor.UnpackGobBlocksInY();
- int gobBlocksInZ = descriptor.UnpackGobBlocksInZ();
-
- int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX();
-
- layerSize = 0;
-
- int minLod = descriptor.UnpackBaseLevel();
- int maxLod = descriptor.UnpackMaxLevelInclusive();
-
- // Linear textures don't support mipmaps, so we don't handle this case here.
- if ((minLod != 0 || maxLod + 1 != levels) && target != Target.TextureBuffer && !isLinear)
- {
- int depth = TextureInfo.GetDepth(target, depthOrLayers);
- int layers = TextureInfo.GetLayers(target, depthOrLayers);
-
- SizeInfo sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
- width,
- height,
- depth,
- levels,
- layers,
- formatInfo.BlockWidth,
- formatInfo.BlockHeight,
- formatInfo.BytesPerPixel,
- gobBlocksInY,
- gobBlocksInZ,
- gobBlocksInTileX);
-
- layerSize = sizeInfo.LayerSize;
-
- if (minLod != 0 && minLod < levels)
- {
- // If the base level is not zero, we additionally add the mip level offset
- // to the address, this allows the texture manager to find the base level from the
- // address if there is a overlapping texture on the cache that can contain the new texture.
- gpuVa += (ulong)sizeInfo.GetMipOffset(minLod);
-
- width = Math.Max(1, width >> minLod);
- height = Math.Max(1, height >> minLod);
-
- if (target == Target.Texture3D)
- {
- depthOrLayers = Math.Max(1, depthOrLayers >> minLod);
- }
-
- (gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ);
- }
-
- levels = (maxLod - minLod) + 1;
- }
-
- 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);
-
- if (formatInfo.Format.IsDepthOrStencil())
- {
- swizzleR = SwizzleComponent.Red;
- swizzleG = SwizzleComponent.Red;
- swizzleB = SwizzleComponent.Red;
-
- if (depthStencilMode == DepthStencilMode.Depth)
- {
- swizzleA = SwizzleComponent.One;
- }
- else
- {
- swizzleA = SwizzleComponent.Red;
- }
- }
-
- return new TextureInfo(
- gpuVa,
- width,
- height,
- depthOrLayers,
- levels,
- samplesInX,
- samplesInY,
- stride,
- isLinear,
- gobBlocksInY,
- gobBlocksInZ,
- gobBlocksInTileX,
- target,
- formatInfo,
- depthStencilMode,
- swizzleR,
- swizzleG,
- swizzleB,
- swizzleA);
- }
-
- /// <summary>
- /// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
- /// The depth-stencil mode is determined based on how the driver sets those parameters.
- /// </summary>
- /// <param name="format">The format of the texture</param>
- /// <param name="components">The texture swizzle components</param>
- /// <returns>The depth-stencil mode</returns>
- 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.D24UnormS8Uint)
- {
- return component == SwizzleComponent.Red
- ? DepthStencilMode.Stencil
- : DepthStencilMode.Depth;
- }
- else
- {
- return component == SwizzleComponent.Red
- ? DepthStencilMode.Depth
- : DepthStencilMode.Stencil;
- }
- }
-
- /// <summary>
- /// Checks if the swizzle component is equal to the red or green channels.
- /// </summary>
- /// <param name="component">The swizzle component to check</param>
- /// <returns>True if the swizzle component is equal to the red or green, false otherwise</returns>
- private static bool IsRG(SwizzleComponent component)
- {
- return component == SwizzleComponent.Red ||
- component == SwizzleComponent.Green;
- }
-
- /// <summary>
- /// Decrements the reference count of the texture.
- /// This indicates that the texture pool is not using it anymore.
- /// </summary>
- /// <param name="item">The texture to be deleted</param>
- protected override void Delete(Texture item)
- {
- item?.DecrementReferenceCount(this);
- }
-
- public override void Dispose()
- {
- ProcessDereferenceQueue();
-
- base.Dispose();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
deleted file mode 100644
index 0017f4cc..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture pool cache.
- /// This can keep multiple texture pools, and return the current one as needed.
- /// It is useful for applications that uses multiple texture pools.
- /// </summary>
- class TexturePoolCache : PoolCache<TexturePool>
- {
- /// <summary>
- /// Constructs a new instance of the texture pool.
- /// </summary>
- /// <param name="context">GPU context that the texture pool belongs to</param>
- public TexturePoolCache(GpuContext context) : base(context)
- {
- }
-
- /// <summary>
- /// Creates a new instance of the texture pool.
- /// </summary>
- /// <param name="context">GPU context that the texture pool belongs to</param>
- /// <param name="channel">GPU channel that the texture pool belongs to</param>
- /// <param name="address">Address of the texture pool in guest memory</param>
- /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
- protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
- {
- return new TexturePool(context, channel, address, maximumId);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs b/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs
deleted file mode 100644
index b937f577..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureScaleMode.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// The scale mode for a given texture.
- /// Blacklisted textures cannot be scaled, Eligible textures have not been scaled yet,
- /// and Scaled textures have been scaled already.
- /// Undesired textures will stay at 1x until a situation where they must match a scaled texture.
- /// </summary>
- enum TextureScaleMode
- {
- Eligible = 0,
- Scaled = 1,
- Blacklisted = 2,
- Undesired = 3
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
deleted file mode 100644
index 890bf173..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture search flags, defines texture information comparison rules.
- /// </summary>
- [Flags]
- enum TextureSearchFlags
- {
- None = 0,
- ForSampler = 1 << 1,
- ForCopy = 1 << 2,
- WithUpscale = 1 << 3,
- NoCreate = 1 << 4
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
deleted file mode 100644
index 5e0a0721..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Shader;
-
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// Texture target.
- /// </summary>
- enum TextureTarget : byte
- {
- Texture1D,
- Texture2D,
- Texture3D,
- Cubemap,
- Texture1DArray,
- Texture2DArray,
- TextureBuffer,
- Texture2DRect,
- CubemapArray
- }
-
- static class TextureTargetConverter
- {
- /// <summary>
- /// Converts the texture target enum to a host compatible, Graphics Abstraction Layer enum.
- /// </summary>
- /// <param name="target">The target enum to convert</param>
- /// <param name="isMultisample">True if the texture is a multisampled texture</param>
- /// <returns>The host compatible texture target</returns>
- 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.Texture2DRect: 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;
- }
-
- /// <summary>
- /// Converts the texture target enum to a shader sampler type.
- /// </summary>
- /// <param name="target">The target enum to convert</param>
- /// <returns>The shader sampler type</returns>
- public static SamplerType ConvertSamplerType(this TextureTarget target)
- {
- return target switch
- {
- TextureTarget.Texture1D => SamplerType.Texture1D,
- TextureTarget.Texture2D => SamplerType.Texture2D,
- TextureTarget.Texture3D => SamplerType.Texture3D,
- TextureTarget.Cubemap => SamplerType.TextureCube,
- TextureTarget.Texture1DArray => SamplerType.Texture1D | SamplerType.Array,
- TextureTarget.Texture2DArray => SamplerType.Texture2D | SamplerType.Array,
- TextureTarget.TextureBuffer => SamplerType.TextureBuffer,
- TextureTarget.Texture2DRect => SamplerType.Texture2D,
- TextureTarget.CubemapArray => SamplerType.TextureCube | SamplerType.Array,
- _ => SamplerType.Texture2D
- };
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs
deleted file mode 100644
index b89936eb..00000000
--- a/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.Image
-{
- /// <summary>
- /// The level of view compatibility one texture has to another.
- /// Values are increasing in compatibility from 0 (incompatible).
- /// </summary>
- enum TextureViewCompatibility
- {
- Incompatible = 0,
- LayoutIncompatible,
- CopyOnly,
- Full
- }
-}