From c6f8bfed904e30f7c5d890a2f0ef531eb9e298e5 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 22 Apr 2024 15:05:55 -0300 Subject: Add support for bindless textures from shader input (vertex buffer) on Vulkan (#6577) * Add support for bindless textures from shader input (vertex buffer) * Shader cache version bump * Format whitespace * Remove cache entries on pool removal, disable for OpenGL * PR feedback --- .../Image/TextureBindingsArrayCache.cs | 616 +++++++++++++++++---- 1 file changed, 503 insertions(+), 113 deletions(-) (limited to 'src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs') diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs index 4645317c..7e486e0a 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs @@ -21,12 +21,98 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly GpuContext _context; private readonly GpuChannel _channel; - private readonly bool _isCompute; /// /// Array cache entry key. /// - private readonly struct CacheEntryKey : IEquatable + private readonly struct CacheEntryFromPoolKey : IEquatable + { + /// + /// Whether the entry is for an image. + /// + public readonly bool IsImage; + + /// + /// Whether the entry is for a sampler. + /// + public readonly bool IsSampler; + + /// + /// Texture or image target type. + /// + public readonly Target Target; + + /// + /// Number of entries of the array. + /// + public readonly int ArrayLength; + + private readonly TexturePool _texturePool; + private readonly SamplerPool _samplerPool; + + /// + /// Creates a new array cache entry. + /// + /// Whether the entry is for an image + /// Binding information for the array + /// Texture pool where the array textures are located + /// Sampler pool where the array samplers are located + public CacheEntryFromPoolKey(bool isImage, TextureBindingInfo bindingInfo, TexturePool texturePool, SamplerPool samplerPool) + { + IsImage = isImage; + IsSampler = bindingInfo.IsSamplerOnly; + Target = bindingInfo.Target; + ArrayLength = bindingInfo.ArrayLength; + + _texturePool = texturePool; + _samplerPool = samplerPool; + } + + /// + /// Checks if the pool matches the cached pool. + /// + /// Texture or sampler pool instance + /// True if the pool matches, false otherwise + public bool MatchesPool(IPool pool) + { + return _texturePool == pool || _samplerPool == pool; + } + + /// + /// Checks if the texture and sampler pools matches the cached pools. + /// + /// Texture pool instance + /// Sampler pool instance + /// True if the pools match, false otherwise + private bool MatchesPools(TexturePool texturePool, SamplerPool samplerPool) + { + return _texturePool == texturePool && _samplerPool == samplerPool; + } + + public bool Equals(CacheEntryFromPoolKey other) + { + return IsImage == other.IsImage && + IsSampler == other.IsSampler && + Target == other.Target && + ArrayLength == other.ArrayLength && + MatchesPools(other._texturePool, other._samplerPool); + } + + public override bool Equals(object obj) + { + return obj is CacheEntryFromBufferKey other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(_texturePool, _samplerPool, IsSampler); + } + } + + /// + /// Array cache entry key. + /// + private readonly struct CacheEntryFromBufferKey : IEquatable { /// /// Whether the entry is for an image. @@ -61,7 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Texture pool where the array textures are located /// Sampler pool where the array samplers are located /// Constant buffer bounds with the texture handles - public CacheEntryKey( + public CacheEntryFromBufferKey( bool isImage, TextureBindingInfo bindingInfo, TexturePool texturePool, @@ -100,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Image return _textureBufferBounds.Equals(textureBufferBounds); } - public bool Equals(CacheEntryKey other) + public bool Equals(CacheEntryFromBufferKey other) { return IsImage == other.IsImage && Target == other.Target && @@ -112,7 +198,7 @@ namespace Ryujinx.Graphics.Gpu.Image public override bool Equals(object obj) { - return obj is CacheEntryKey other && Equals(other); + return obj is CacheEntryFromBufferKey other && Equals(other); } public override int GetHashCode() @@ -122,88 +208,58 @@ namespace Ryujinx.Graphics.Gpu.Image } /// - /// Array cache entry. + /// Array cache entry from pool. /// private class CacheEntry { - /// - /// Key for this entry on the cache. - /// - public readonly CacheEntryKey Key; - - /// - /// Linked list node used on the texture bindings array cache. - /// - public LinkedListNode CacheNode; - - /// - /// Timestamp set on the last use of the array by the cache. - /// - public int CacheTimestamp; - /// /// All cached textures, along with their invalidated sequence number as value. /// public readonly Dictionary Textures; /// - /// All pool texture IDs along with their textures. + /// Backend texture array if the entry is for a texture, otherwise null. /// - public readonly Dictionary TextureIds; + public readonly ITextureArray TextureArray; /// - /// All pool sampler IDs along with their samplers. + /// Backend image array if the entry is for an image, otherwise null. /// - public readonly Dictionary SamplerIds; + public readonly IImageArray ImageArray; /// - /// Backend texture array if the entry is for a texture, otherwise null. + /// Texture pool where the array textures are located. /// - public readonly ITextureArray TextureArray; + protected readonly TexturePool TexturePool; /// - /// Backend image array if the entry is for an image, otherwise null. + /// Sampler pool where the array samplers are located. /// - public readonly IImageArray ImageArray; - - private readonly TexturePool _texturePool; - private readonly SamplerPool _samplerPool; + protected readonly SamplerPool SamplerPool; private int _texturePoolSequence; private int _samplerPoolSequence; - private int[] _cachedTextureBuffer; - private int[] _cachedSamplerBuffer; - - private int _lastSequenceNumber; - /// /// Creates a new array cache entry. /// - /// Key for this entry on the cache /// Texture pool where the array textures are located /// Sampler pool where the array samplers are located - private CacheEntry(ref CacheEntryKey key, TexturePool texturePool, SamplerPool samplerPool) + private CacheEntry(TexturePool texturePool, SamplerPool samplerPool) { - Key = key; Textures = new Dictionary(); - TextureIds = new Dictionary(); - SamplerIds = new Dictionary(); - _texturePool = texturePool; - _samplerPool = samplerPool; - - _lastSequenceNumber = -1; + TexturePool = texturePool; + SamplerPool = samplerPool; } /// /// Creates a new array cache entry. /// - /// Key for this entry on the cache /// Backend texture array /// Texture pool where the array textures are located /// Sampler pool where the array samplers are located - public CacheEntry(ref CacheEntryKey key, ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : this(ref key, texturePool, samplerPool) + public CacheEntry(ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : this(texturePool, samplerPool) { TextureArray = array; } @@ -211,11 +267,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Creates a new array cache entry. /// - /// Key for this entry on the cache /// Backend image array /// Texture pool where the array textures are located /// Sampler pool where the array samplers are located - public CacheEntry(ref CacheEntryKey key, IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : this(ref key, texturePool, samplerPool) + public CacheEntry(IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : this(texturePool, samplerPool) { ImageArray = array; } @@ -248,23 +303,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Clears all cached texture instances. /// - public void Reset() + public virtual void Reset() { Textures.Clear(); - TextureIds.Clear(); - SamplerIds.Clear(); - } - - /// - /// Updates the cached constant buffer data. - /// - /// Constant buffer data with the texture handles (and sampler handles, if they are combined) - /// Constant buffer data with the sampler handles - /// Whether and comes from different buffers - public void UpdateData(ReadOnlySpan cachedTextureBuffer, ReadOnlySpan cachedSamplerBuffer, bool separateSamplerBuffer) - { - _cachedTextureBuffer = cachedTextureBuffer.ToArray(); - _cachedSamplerBuffer = separateSamplerBuffer ? cachedSamplerBuffer.ToArray() : _cachedTextureBuffer; } /// @@ -287,39 +328,105 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Checks if the cached texture or sampler pool has been modified since the last call to this method. /// - /// True if any used entries of the pools might have been modified, false otherwise - public bool PoolsModified() + /// True if any used entries of the pool might have been modified, false otherwise + public bool TexturePoolModified() { - bool texturePoolModified = _texturePool.WasModified(ref _texturePoolSequence); - bool samplerPoolModified = _samplerPool.WasModified(ref _samplerPoolSequence); + return TexturePool.WasModified(ref _texturePoolSequence); + } - // If both pools were not modified since the last check, we have nothing else to check. - if (!texturePoolModified && !samplerPoolModified) - { - return false; - } + /// + /// Checks if the cached texture or sampler pool has been modified since the last call to this method. + /// + /// True if any used entries of the pool might have been modified, false otherwise + public bool SamplerPoolModified() + { + return SamplerPool.WasModified(ref _samplerPoolSequence); + } + } - // If the pools were modified, let's check if any of the entries we care about changed. + /// + /// Array cache entry from constant buffer. + /// + private class CacheEntryFromBuffer : CacheEntry + { + /// + /// Key for this entry on the cache. + /// + public readonly CacheEntryFromBufferKey Key; - // Check if any of our cached textures changed on the pool. - foreach ((int textureId, Texture texture) in TextureIds) - { - if (_texturePool.GetCachedItem(textureId) != texture) - { - return true; - } - } + /// + /// Linked list node used on the texture bindings array cache. + /// + public LinkedListNode CacheNode; - // Check if any of our cached samplers changed on the pool. - foreach ((int samplerId, Sampler sampler) in SamplerIds) - { - if (_samplerPool.GetCachedItem(samplerId) != sampler) - { - return true; - } - } + /// + /// Timestamp set on the last use of the array by the cache. + /// + public int CacheTimestamp; - return false; + /// + /// All pool texture IDs along with their textures. + /// + public readonly Dictionary TextureIds; + + /// + /// All pool sampler IDs along with their samplers. + /// + public readonly Dictionary SamplerIds; + + private int[] _cachedTextureBuffer; + private int[] _cachedSamplerBuffer; + + private int _lastSequenceNumber; + + /// + /// Creates a new array cache entry. + /// + /// Key for this entry on the cache + /// Backend texture array + /// Texture pool where the array textures are located + /// Sampler pool where the array samplers are located + public CacheEntryFromBuffer(ref CacheEntryFromBufferKey key, ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : base(array, texturePool, samplerPool) + { + Key = key; + _lastSequenceNumber = -1; + TextureIds = new Dictionary(); + SamplerIds = new Dictionary(); + } + + /// + /// Creates a new array cache entry. + /// + /// Key for this entry on the cache + /// Backend image array + /// Texture pool where the array textures are located + /// Sampler pool where the array samplers are located + public CacheEntryFromBuffer(ref CacheEntryFromBufferKey key, IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : base(array, texturePool, samplerPool) + { + Key = key; + _lastSequenceNumber = -1; + TextureIds = new Dictionary(); + SamplerIds = new Dictionary(); + } + + /// + public override void Reset() + { + base.Reset(); + TextureIds.Clear(); + SamplerIds.Clear(); + } + + /// + /// Updates the cached constant buffer data. + /// + /// Constant buffer data with the texture handles (and sampler handles, if they are combined) + /// Constant buffer data with the sampler handles + /// Whether and comes from different buffers + public void UpdateData(ReadOnlySpan cachedTextureBuffer, ReadOnlySpan cachedSamplerBuffer, bool separateSamplerBuffer) + { + _cachedTextureBuffer = cachedTextureBuffer.ToArray(); + _cachedSamplerBuffer = separateSamplerBuffer ? cachedSamplerBuffer.ToArray() : _cachedTextureBuffer; } /// @@ -380,10 +487,51 @@ namespace Ryujinx.Graphics.Gpu.Image return true; } + + /// + /// Checks if the cached texture or sampler pool has been modified since the last call to this method. + /// + /// True if any used entries of the pools might have been modified, false otherwise + public bool PoolsModified() + { + bool texturePoolModified = TexturePoolModified(); + bool samplerPoolModified = SamplerPoolModified(); + + // If both pools were not modified since the last check, we have nothing else to check. + if (!texturePoolModified && !samplerPoolModified) + { + return false; + } + + // If the pools were modified, let's check if any of the entries we care about changed. + + // Check if any of our cached textures changed on the pool. + foreach ((int textureId, (Texture texture, TextureDescriptor descriptor)) in TextureIds) + { + if (TexturePool.GetCachedItem(textureId) != texture || + (texture == null && TexturePool.IsValidId(textureId) && !TexturePool.GetDescriptorRef(textureId).Equals(descriptor))) + { + return true; + } + } + + // Check if any of our cached samplers changed on the pool. + foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds) + { + if (SamplerPool.GetCachedItem(samplerId) != sampler || + (sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor))) + { + return true; + } + } + + return false; + } } - private readonly Dictionary _cache; - private readonly LinkedList _lruCache; + private readonly Dictionary _cacheFromBuffer; + private readonly Dictionary _cacheFromPool; + private readonly LinkedList _lruCache; private int _currentTimestamp; @@ -392,14 +540,13 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// GPU context /// GPU channel - /// Whether the bindings will be used for compute or graphics pipelines - public TextureBindingsArrayCache(GpuContext context, GpuChannel channel, bool isCompute) + public TextureBindingsArrayCache(GpuContext context, GpuChannel channel) { _context = context; _channel = channel; - _isCompute = isCompute; - _cache = new Dictionary(); - _lruCache = new LinkedList(); + _cacheFromBuffer = new Dictionary(); + _cacheFromPool = new Dictionary(); + _lruCache = new LinkedList(); } /// @@ -457,15 +604,180 @@ namespace Ryujinx.Graphics.Gpu.Image bool isImage, SamplerIndex samplerIndex, TextureBindingInfo bindingInfo) + { + if (IsDirectHandleType(bindingInfo.Handle)) + { + UpdateFromPool(texturePool, samplerPool, stage, isImage, bindingInfo); + } + else + { + UpdateFromBuffer(texturePool, samplerPool, stage, stageIndex, textureBufferIndex, isImage, samplerIndex, bindingInfo); + } + } + + /// + /// Updates a texture or image array bindings and textures from a texture or sampler pool. + /// + /// Texture pool + /// Sampler pool + /// Shader stage where the array is used + /// Whether the array is a image or texture array + /// Array binding information + private void UpdateFromPool(TexturePool texturePool, SamplerPool samplerPool, ShaderStage stage, bool isImage, TextureBindingInfo bindingInfo) + { + CacheEntry entry = GetOrAddEntry(texturePool, samplerPool, bindingInfo, isImage, out bool isNewEntry); + + bool isSampler = bindingInfo.IsSamplerOnly; + bool poolModified = isSampler ? entry.SamplerPoolModified() : entry.TexturePoolModified(); + bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); + bool resScaleUnsupported = bindingInfo.Flags.HasFlag(TextureUsageFlags.ResScaleUnsupported); + + if (!poolModified && !isNewEntry && entry.ValidateTextures()) + { + entry.SynchronizeMemory(isStore, resScaleUnsupported); + + if (isImage) + { + _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); + } + else + { + _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); + } + + return; + } + + if (!isNewEntry) + { + entry.Reset(); + } + + int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1; + length = Math.Min(length, bindingInfo.ArrayLength); + + Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null; + ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength]; + ITexture[] textures = new ITexture[bindingInfo.ArrayLength]; + + for (int index = 0; index < length; index++) + { + Texture texture = null; + Sampler sampler = null; + + if (isSampler) + { + sampler = samplerPool?.Get(index); + } + else + { + ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, out texture); + + if (texture != null) + { + entry.Textures[texture] = texture.InvalidatedSequence; + + if (isStore) + { + texture.SignalModified(); + } + + if (resScaleUnsupported && texture.ScaleMode != TextureScaleMode.Blacklisted) + { + // Scaling textures used on arrays is currently not supported. + + texture.BlacklistScale(); + } + } + } + + ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); + ISampler hostSampler = sampler?.GetHostSampler(texture); + + Format format = bindingInfo.Format; + + 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 accommodate larger data, so we need to re-bind + // to ensure we're not using a old buffer that was already deleted. + if (isImage) + { + if (format == 0 && texture != null) + { + format = texture.Format; + } + + _channel.BufferManager.SetBufferTextureStorage(entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format); + } + else + { + _channel.BufferManager.SetBufferTextureStorage(entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format); + } + } + else if (isImage) + { + if (format == 0 && texture != null) + { + format = texture.Format; + } + + formats[index] = format; + textures[index] = hostTexture; + } + else + { + samplers[index] = hostSampler; + textures[index] = hostTexture; + } + } + + if (isImage) + { + entry.ImageArray.SetFormats(0, formats); + entry.ImageArray.SetImages(0, textures); + + _context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray); + } + else + { + entry.TextureArray.SetSamplers(0, samplers); + entry.TextureArray.SetTextures(0, textures); + + _context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray); + } + } + + /// + /// Updates a texture or image array bindings and textures from constant buffer handles. + /// + /// Texture pool + /// Sampler pool + /// Shader stage where the array is used + /// Shader stage index where the array is used + /// Texture constant buffer index + /// Whether the array is a image or texture array + /// Sampler handles source + /// Array binding information + private void UpdateFromBuffer( + TexturePool texturePool, + SamplerPool samplerPool, + ShaderStage stage, + int stageIndex, + int textureBufferIndex, + bool isImage, + SamplerIndex samplerIndex, + TextureBindingInfo bindingInfo) { (textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, textureBufferIndex); bool separateSamplerBuffer = textureBufferIndex != samplerBufferIndex; + bool isCompute = stage == ShaderStage.Compute; - ref BufferBounds textureBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex); - ref BufferBounds samplerBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex); + ref BufferBounds textureBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex); + ref BufferBounds samplerBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex); - CacheEntry entry = GetOrAddEntry( + CacheEntryFromBuffer entry = GetOrAddEntry( texturePool, samplerPool, bindingInfo, @@ -589,8 +901,8 @@ namespace Ryujinx.Graphics.Gpu.Image Sampler sampler = samplerPool?.Get(samplerId); - entry.TextureIds[textureId] = texture; - entry.SamplerIds[samplerId] = sampler; + entry.TextureIds[textureId] = (texture, descriptor); + entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default); ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); ISampler hostSampler = sampler?.GetHostSampler(texture); @@ -650,16 +962,57 @@ namespace Ryujinx.Graphics.Gpu.Image } /// - /// Gets a cached texture entry, or creates a new one if not found. + /// Gets a cached texture entry from pool, or creates a new one if not found. /// /// Texture pool /// Sampler pool /// Array binding information /// Whether the array is a image or texture array - /// Constant buffer bounds with the texture handles /// Whether a new entry was created, or an existing one was returned /// Cache entry private CacheEntry GetOrAddEntry( + TexturePool texturePool, + SamplerPool samplerPool, + TextureBindingInfo bindingInfo, + bool isImage, + out bool isNew) + { + CacheEntryFromPoolKey key = new CacheEntryFromPoolKey(isImage, bindingInfo, texturePool, samplerPool); + + isNew = !_cacheFromPool.TryGetValue(key, out CacheEntry entry); + + if (isNew) + { + int arrayLength = bindingInfo.ArrayLength; + + if (isImage) + { + IImageArray array = _context.Renderer.CreateImageArray(arrayLength, bindingInfo.Target == Target.TextureBuffer); + + _cacheFromPool.Add(key, entry = new CacheEntry(array, texturePool, samplerPool)); + } + else + { + ITextureArray array = _context.Renderer.CreateTextureArray(arrayLength, bindingInfo.Target == Target.TextureBuffer); + + _cacheFromPool.Add(key, entry = new CacheEntry(array, texturePool, samplerPool)); + } + } + + return entry; + } + + /// + /// Gets a cached texture entry from constant buffer, or creates a new one if not found. + /// + /// Texture pool + /// Sampler pool + /// Array binding information + /// Whether the array is a image or texture array + /// Constant buffer bounds with the texture handles + /// Whether a new entry was created, or an existing one was returned + /// Cache entry + private CacheEntryFromBuffer GetOrAddEntry( TexturePool texturePool, SamplerPool samplerPool, TextureBindingInfo bindingInfo, @@ -667,14 +1020,14 @@ namespace Ryujinx.Graphics.Gpu.Image ref BufferBounds textureBufferBounds, out bool isNew) { - CacheEntryKey key = new CacheEntryKey( + CacheEntryFromBufferKey key = new CacheEntryFromBufferKey( isImage, bindingInfo, texturePool, samplerPool, ref textureBufferBounds); - isNew = !_cache.TryGetValue(key, out CacheEntry entry); + isNew = !_cacheFromBuffer.TryGetValue(key, out CacheEntryFromBuffer entry); if (isNew) { @@ -684,13 +1037,13 @@ namespace Ryujinx.Graphics.Gpu.Image { IImageArray array = _context.Renderer.CreateImageArray(arrayLength, bindingInfo.Target == Target.TextureBuffer); - _cache.Add(key, entry = new CacheEntry(ref key, array, texturePool, samplerPool)); + _cacheFromBuffer.Add(key, entry = new CacheEntryFromBuffer(ref key, array, texturePool, samplerPool)); } else { ITextureArray array = _context.Renderer.CreateTextureArray(arrayLength, bindingInfo.Target == Target.TextureBuffer); - _cache.Add(key, entry = new CacheEntry(ref key, array, texturePool, samplerPool)); + _cacheFromBuffer.Add(key, entry = new CacheEntryFromBuffer(ref key, array, texturePool, samplerPool)); } } @@ -716,15 +1069,52 @@ namespace Ryujinx.Graphics.Gpu.Image /// private void RemoveLeastUsedEntries() { - LinkedListNode nextNode = _lruCache.First; + LinkedListNode nextNode = _lruCache.First; while (nextNode != null && _currentTimestamp - nextNode.Value.CacheTimestamp >= MinDeltaForRemoval) { - LinkedListNode toRemove = nextNode; + LinkedListNode toRemove = nextNode; nextNode = nextNode.Next; - _cache.Remove(toRemove.Value.Key); + _cacheFromBuffer.Remove(toRemove.Value.Key); _lruCache.Remove(toRemove); } } + + /// + /// Removes all cached texture arrays matching the specified texture pool. + /// + /// Texture pool + public void RemoveAllWithPool(IPool pool) + { + List keysToRemove = null; + + foreach (CacheEntryFromPoolKey key in _cacheFromPool.Keys) + { + if (key.MatchesPool(pool)) + { + (keysToRemove ??= new()).Add(key); + } + } + + if (keysToRemove != null) + { + foreach (CacheEntryFromPoolKey key in keysToRemove) + { + _cacheFromPool.Remove(key); + } + } + } + + /// + /// Checks if a handle indicates the binding should have all its textures sourced directly from a pool. + /// + /// Handle to check + /// True if the handle represents direct pool access, false otherwise + private static bool IsDirectHandleType(int handle) + { + (_, _, TextureHandleType type) = TextureHandle.UnpackOffsets(handle); + + return type == TextureHandleType.Direct; + } } } -- cgit v1.2.3