diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Graphics.Gpu/Image | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image')
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 - } -} |
