From 3c3bcd82fe6dfa8bdc2c9a9f33724ebfacd7dd40 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 27 Jul 2022 21:07:48 -0300 Subject: Add a sampler pool cache and improve texture pool cache (#3487) * Add a sampler pool cache and improve texture pool cache * Increase disposal timestamp delta more to be on the safe side * Nits * Use abstract class for PoolCache, remove factory callback --- .../Image/TextureBindingsManager.cs | 164 ++++++++++++--------- 1 file changed, 93 insertions(+), 71 deletions(-) (limited to 'Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs') 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 /// /// Texture bindings manager. /// - 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 /// /// The GPU context that the texture bindings manager belongs to /// The GPU channel that the texture bindings manager belongs to - /// Texture pools cache used to get texture pools from + /// Texture pools cache used to get texture pools from + /// Sampler pools cache used to get sampler pools from /// Array where the scales for the currently bound textures are stored /// True if the bindings manager is used for the compute engine - 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 /// Type of the sampler pool indexing used for bound samplers 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; } /// @@ -201,18 +197,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// Maximum ID of the pool (total count minus one) 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; } /// @@ -222,13 +209,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// ID of the sampler 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)); } /// @@ -340,13 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if all bound textures match the current shader specialiation state, false otherwise 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. /// - /// The current texture pool + /// The current texture pool + /// The current sampler pool /// The shader stage using the textures to be bound /// The stage number of the specified shader stageTrue if either the texture or sampler pool was modified, false otherwise /// Specialization state for the bound shader /// True if all bound textures match the current shader specialiation state, false otherwise - 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 } /// - /// 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. /// - public void Rebind() + /// The texture and sampler pools + 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); + } + + /// + /// Forces the texture and sampler pools to be re-loaded from the cache on next use. + /// + /// + /// This should be called if the memory mappings change, to ensure the correct pools are being used. + /// + public void ReloadPools() + { + _samplerPool = null; + _texturePool = null; } /// - /// Disposes all textures and samplers in the cache. + /// Force all bound textures and images to be rebound the next time CommitBindings is called. /// - public void Dispose() + public void Rebind() { - _samplerPool?.Dispose(); + Array.Clear(_textureState); + Array.Clear(_imageState); } } } \ No newline at end of file -- cgit v1.2.3