diff options
-rw-r--r-- | Ryujinx.Graphics.Gpu/GpuChannel.cs | 15 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/PoolCache.cs | 129 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/SamplerPool.cs | 15 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs | 30 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs | 164 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 27 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 11 | ||||
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs | 64 |
8 files changed, 320 insertions, 135 deletions
diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs index b9d91f93..f3bdd576 100644 --- a/Ryujinx.Graphics.Gpu/GpuChannel.cs +++ b/Ryujinx.Graphics.Gpu/GpuChannel.cs @@ -59,9 +59,24 @@ namespace Ryujinx.Graphics.Gpu { oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind; oldMemoryManager.Physical.DecrementReferenceCount(); + oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler; } memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind; + memoryManager.MemoryUnmapped += MemoryUnmappedHandler; + + // Since the memory manager changed, make sure we will get pools from addresses of the new memory manager. + TextureManager.ReloadPools(); + } + + /// <summary> + /// Memory mappings change event handler. + /// </summary> + /// <param name="sender">Memory manager where the mappings changed</param> + /// <param name="e">Information about the region that is being changed</param> + private void MemoryUnmappedHandler(object sender, UnmapEventArgs e) + { + TextureManager.ReloadPools(); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Image/PoolCache.cs b/Ryujinx.Graphics.Gpu/Image/PoolCache.cs new file mode 100644 index 00000000..e1493f38 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/PoolCache.cs @@ -0,0 +1,129 @@ +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/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs index e95800ad..eb7222f9 100644 --- a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs +++ b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs @@ -1,16 +1,27 @@ using Ryujinx.Graphics.Gpu.Memory; +using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Image { /// <summary> /// Sampler pool. /// </summary> - class SamplerPool : Pool<Sampler, SamplerDescriptor> + class SamplerPool : Pool<Sampler, SamplerDescriptor>, IPool<SamplerPool> { private float _forcedAnisotropy; /// <summary> - /// Constructs a new instance of the sampler pool. + /// 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> diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs new file mode 100644 index 00000000..3b3350fb --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs @@ -0,0 +1,30 @@ +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/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index fcd23441..067a1f9f 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <summary> /// Texture bindings manager. /// </summary> - class TextureBindingsManager : IDisposable + class TextureBindingsManager { private const int InitialTextureStateSize = 32; private const int InitialImageStateSize = 8; @@ -22,15 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly bool _isCompute; - private SamplerPool _samplerPool; - + private ulong _texturePoolGpuVa; + private int _texturePoolMaximumId; + private TexturePool _texturePool; + private ulong _samplerPoolGpuVa; + private int _samplerPoolMaximumId; private SamplerIndex _samplerIndex; - - private ulong _texturePoolAddress; - private int _texturePoolMaximumId; + private SamplerPool _samplerPool; private readonly GpuChannel _channel; private readonly TexturePoolCache _texturePoolCache; + private readonly SamplerPoolCache _samplerPoolCache; private TexturePool _cachedTexturePool; private SamplerPool _cachedSamplerPool; @@ -72,16 +74,25 @@ namespace Ryujinx.Graphics.Gpu.Image /// </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="poolCache">Texture pools cache used to get texture pools from</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 poolCache, float[] scales, bool isCompute) + public TextureBindingsManager( + GpuContext context, + GpuChannel channel, + TexturePoolCache texturePoolCache, + SamplerPoolCache samplerPoolCache, + float[] scales, + bool isCompute) { - _context = context; - _channel = channel; - _texturePoolCache = poolCache; - _scales = scales; - _isCompute = isCompute; + _context = context; + _channel = channel; + _texturePoolCache = texturePoolCache; + _samplerPoolCache = samplerPoolCache; + + _scales = scales; + _isCompute = isCompute; int stages = isCompute ? 1 : Constants.ShaderStages; @@ -173,25 +184,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param> public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex) { - if (gpuVa != 0) - { - ulong address = _channel.MemoryManager.Translate(gpuVa); - - if (_samplerPool != null && _samplerPool.Address == address && _samplerPool.MaximumId >= maximumId) - { - return; - } - - _samplerPool?.Dispose(); - _samplerPool = new SamplerPool(_context, _channel.MemoryManager.Physical, address, maximumId); - } - else - { - _samplerPool?.Dispose(); - _samplerPool = null; - } - + _samplerPoolGpuVa = gpuVa; + _samplerPoolMaximumId = maximumId; _samplerIndex = samplerIndex; + _samplerPool = null; } /// <summary> @@ -201,18 +197,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="maximumId">Maximum ID of the pool (total count minus one)</param> public void SetTexturePool(ulong gpuVa, int maximumId) { - if (gpuVa != 0) - { - ulong address = _channel.MemoryManager.Translate(gpuVa); - - _texturePoolAddress = address; - _texturePoolMaximumId = maximumId; - } - else - { - _texturePoolAddress = 0; - _texturePoolMaximumId = 0; - } + _texturePoolGpuVa = gpuVa; + _texturePoolMaximumId = maximumId; + _texturePool = null; } /// <summary> @@ -222,13 +209,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="samplerId">ID of the sampler</param> public (Texture, Sampler) GetTextureAndSampler(int textureId, int samplerId) { - ulong texturePoolAddress = _texturePoolAddress; - - TexturePool texturePool = texturePoolAddress != 0 - ? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId) - : null; + (TexturePool texturePool, SamplerPool samplerPool) = GetPools(); - return (texturePool.Get(textureId), _samplerPool.Get(samplerId)); + return (texturePool.Get(textureId), samplerPool.Get(samplerId)); } /// <summary> @@ -340,13 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns> public bool CommitBindings(ShaderSpecializationState specState) { - ulong texturePoolAddress = _texturePoolAddress; - - TexturePool texturePool = texturePoolAddress != 0 - ? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId) - : null; - - SamplerPool samplerPool = _samplerPool; + (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. @@ -381,7 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (_isCompute) { - specStateMatches &= CommitTextureBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState); + specStateMatches &= CommitTextureBindings(texturePool, samplerPool, ShaderStage.Compute, 0, poolModified, specState); specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState); } else @@ -390,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Image { int stageIndex = (int)stage - 1; - specStateMatches &= CommitTextureBindings(texturePool, stage, stageIndex, poolModified, specState); + specStateMatches &= CommitTextureBindings(texturePool, samplerPool, stage, stageIndex, poolModified, specState); specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState); } } @@ -447,13 +424,20 @@ namespace Ryujinx.Graphics.Gpu.Image /// 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="pool">The current texture pool</param> + /// <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 pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState) + private bool CommitTextureBindings( + TexturePool texturePool, + SamplerPool samplerPool, + ShaderStage stage, + int stageIndex, + bool poolModified, + ShaderSpecializationState specState) { int textureCount = _textureBindingsCount[stageIndex]; if (textureCount == 0) @@ -461,9 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image return true; } - var samplerPool = _samplerPool; - - if (pool == null) + if (texturePool == null) { Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set."); return true; @@ -528,7 +510,7 @@ namespace Ryujinx.Graphics.Gpu.Image state.TextureHandle = textureId; state.SamplerHandle = samplerId; - ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture); + ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture); specStateMatches &= specState.MatchesTexture(stage, index, descriptor); @@ -820,20 +802,60 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <summary> - /// Force all bound textures and images to be rebound the next time CommitBindings is called. + /// Gets the texture and sampler pool for the GPU virtual address that are currently set. /// </summary> - public void Rebind() + /// <returns>The texture and sampler pools</returns> + private (TexturePool, SamplerPool) GetPools() { - Array.Clear(_textureState); - Array.Clear(_imageState); + 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> - /// Disposes all textures and samplers in the cache. + /// Force all bound textures and images to be rebound the next time CommitBindings is called. /// </summary> - public void Dispose() + public void Rebind() { - _samplerPool?.Dispose(); + Array.Clear(_textureState); + Array.Clear(_imageState); } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 628c3159..fe0175a6 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly TextureBindingsManager _cpBindingsManager; private readonly TextureBindingsManager _gpBindingsManager; private readonly TexturePoolCache _texturePoolCache; + private readonly SamplerPoolCache _samplerPoolCache; private readonly Texture[] _rtColors; private readonly ITexture[] _rtHostColors; @@ -41,13 +42,15 @@ namespace Ryujinx.Graphics.Gpu.Image _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, scales, isCompute: true); - _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: false); + _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]; @@ -368,6 +371,10 @@ namespace Ryujinx.Graphics.Gpu.Image // 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(); @@ -382,6 +389,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// <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(); @@ -502,6 +512,15 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <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> @@ -523,8 +542,8 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> public void Dispose() { - _cpBindingsManager.Dispose(); - _gpBindingsManager.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++) { diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 75974c43..4d2544e2 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -10,19 +10,24 @@ namespace Ryujinx.Graphics.Gpu.Image /// <summary> /// Texture pool. /// </summary> - class TexturePool : Pool<Texture, TextureDescriptor> + class TexturePool : Pool<Texture, TextureDescriptor>, IPool<TexturePool> { private readonly GpuChannel _channel; private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>(); private TextureDescriptor _defaultDescriptor; /// <summary> - /// Intrusive linked list node used on the texture pool cache. + /// Linked list node used on the texture pool cache. /// </summary> public LinkedListNode<TexturePool> CacheNode { get; set; } /// <summary> - /// Constructs a new instance of the texture pool. + /// 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> diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs index 99c5a88b..0017f4cc 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace Ryujinx.Graphics.Gpu.Image { /// <summary> @@ -8,69 +5,26 @@ namespace Ryujinx.Graphics.Gpu.Image /// 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 + class TexturePoolCache : PoolCache<TexturePool> { - private const int MaxCapacity = 4; - - private readonly GpuContext _context; - private readonly LinkedList<TexturePool> _pools; - /// <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) + public TexturePoolCache(GpuContext context) : base(context) { - _context = context; - _pools = new LinkedList<TexturePool>(); } /// <summary> - /// Finds a cache texture pool, or creates a new one if not found. + /// Creates a new instance of the texture pool. /// </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 TexturePool FindOrCreate(GpuChannel channel, ulong address, int maximumId) + /// <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) { - TexturePool pool; - - // First we try to find the pool. - for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next) - { - pool = node.Value; - - if (pool.Address == address) - { - if (pool.CacheNode != _pools.Last) - { - _pools.Remove(pool.CacheNode); - - pool.CacheNode = _pools.AddLast(pool); - } - - return pool; - } - } - - // If not found, create a new one. - pool = new TexturePool(_context, channel, address, maximumId); - - pool.CacheNode = _pools.AddLast(pool); - - if (_pools.Count > MaxCapacity) - { - TexturePool oldestPool = _pools.First.Value; - - _pools.RemoveFirst(); - - oldestPool.Dispose(); - - oldestPool.CacheNode = null; - } - - return pool; + return new TexturePool(context, channel, address, maximumId); } } }
\ No newline at end of file |