aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs290
1 files changed, 215 insertions, 75 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index 7ac4e12e..f15f8885 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -1,8 +1,12 @@
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
{
@@ -35,17 +39,24 @@ namespace Ryujinx.Graphics.Gpu.Image
{
public ITexture Texture;
public ISampler Sampler;
+
+ public int TextureHandle;
+ public int SamplerHandle;
+ public int InvalidatedSequence;
+ public Texture CachedTexture;
+ public Sampler CachedSampler;
}
- private readonly TextureStatePerStage[][] _textureState;
- private readonly TextureStatePerStage[][] _imageState;
+ private TextureStatePerStage[] _textureState;
+ private TextureStatePerStage[] _imageState;
private int[] _textureBindingsCount;
private int[] _imageBindingsCount;
- private int _textureBufferIndex;
+ private int _texturePoolSequence;
+ private int _samplerPoolSequence;
- private bool _rebind;
+ private int _textureBufferIndex;
private readonly float[] _scales;
private bool _scaleChanged;
@@ -72,8 +83,8 @@ namespace Ryujinx.Graphics.Gpu.Image
_textureBindings = new TextureBindingInfo[stages][];
_imageBindings = new TextureBindingInfo[stages][];
- _textureState = new TextureStatePerStage[stages][];
- _imageState = new TextureStatePerStage[stages][];
+ _textureState = new TextureStatePerStage[InitialTextureStateSize];
+ _imageState = new TextureStatePerStage[InitialImageStateSize];
_textureBindingsCount = new int[stages];
_imageBindingsCount = new int[stages];
@@ -82,9 +93,6 @@ namespace Ryujinx.Graphics.Gpu.Image
{
_textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize];
_imageBindings[stage] = new TextureBindingInfo[InitialImageStateSize];
-
- _textureState[stage] = new TextureStatePerStage[InitialTextureStateSize];
- _imageState[stage] = new TextureStatePerStage[InitialImageStateSize];
}
}
@@ -99,15 +107,6 @@ namespace Ryujinx.Graphics.Gpu.Image
if (count > _textureBindings[stage].Length)
{
Array.Resize(ref _textureBindings[stage], count);
- Array.Resize(ref _textureState[stage], count);
- }
-
- int toClear = Math.Max(_textureBindingsCount[stage], count);
- TextureStatePerStage[] state = _textureState[stage];
-
- for (int i = 0; i < toClear; i++)
- {
- state[i] = new TextureStatePerStage();
}
_textureBindingsCount[stage] = count;
@@ -126,20 +125,29 @@ namespace Ryujinx.Graphics.Gpu.Image
if (count > _imageBindings[stage].Length)
{
Array.Resize(ref _imageBindings[stage], count);
- Array.Resize(ref _imageState[stage], count);
}
- int toClear = Math.Max(_imageBindingsCount[stage], count);
- TextureStatePerStage[] state = _imageState[stage];
+ _imageBindingsCount[stage] = count;
+
+ return _imageBindings[stage];
+ }
- for (int i = 0; i < toClear; i++)
+ /// <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)
{
- state[i] = new TextureStatePerStage();
+ Array.Resize(ref _textureState, maxTextureBinding + 1);
}
- _imageBindingsCount[stage] = count;
-
- return _imageBindings[stage];
+ if (maxImageBinding >= _imageState.Length)
+ {
+ Array.Resize(ref _imageState, maxImageBinding + 1);
+ }
}
/// <summary>
@@ -323,7 +331,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Ensures that the bindings are visible to the host GPU.
/// Note: this actually performs the binding using the host graphics API.
/// </summary>
- public void CommitBindings()
+ /// <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)
{
ulong texturePoolAddress = _texturePoolAddress;
@@ -331,10 +341,38 @@ namespace Ryujinx.Graphics.Gpu.Image
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
: null;
+ // 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.
+ 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)
{
- CommitTextureBindings(texturePool, ShaderStage.Compute, 0);
- CommitImageBindings (texturePool, ShaderStage.Compute, 0);
+ specStateMatches &= CommitTextureBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
+ specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
}
else
{
@@ -342,14 +380,57 @@ namespace Ryujinx.Graphics.Gpu.Image
{
int stageIndex = (int)stage - 1;
- CommitTextureBindings(texturePool, stage, stageIndex);
- CommitImageBindings (texturePool, stage, stageIndex);
+ specStateMatches &= CommitTextureBindings(texturePool, stage, stageIndex, poolModified, specState);
+ specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState);
}
}
CommitRenderScale();
- _rebind = false;
+ 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,
+ ref int cachedTextureBufferIndex,
+ ref int cachedSamplerBufferIndex,
+ ref ReadOnlySpan<int> cachedTextureBuffer,
+ 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>
@@ -358,13 +439,16 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </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>
- private void CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex)
+ /// <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)
{
int textureCount = _textureBindingsCount[stageIndex];
if (textureCount == 0)
{
- return;
+ return true;
}
var samplerPool = _samplerPool;
@@ -372,17 +456,26 @@ namespace Ryujinx.Graphics.Gpu.Image
if (pool == null)
{
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
- return;
+ 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];
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
- int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
- int textureId = UnpackTextureId(packedId);
+ 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)
@@ -391,10 +484,30 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
- samplerId = UnpackSamplerId(packedId);
+ samplerId = TextureHandle.UnpackSamplerId(packedId);
}
- Texture texture = pool.Get(textureId);
+ ref TextureStatePerStage 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();
+
+ continue;
+ }
+
+ state.TextureHandle = textureId;
+ state.SamplerHandle = samplerId;
+
+ ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
+
+ specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
@@ -407,30 +520,36 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
- if (_textureState[stageIndex][index].Texture != hostTexture || _rebind)
+ if (state.Texture != hostTexture)
{
if (UpdateScale(texture, bindingInfo, index, stage))
{
hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
}
- _textureState[stageIndex][index].Texture = hostTexture;
+ state.Texture = hostTexture;
_context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
}
Sampler sampler = samplerPool?.Get(samplerId);
+ state.CachedSampler = sampler;
ISampler hostSampler = sampler?.GetHostSampler(texture);
- if (_textureState[stageIndex][index].Sampler != hostSampler || _rebind)
+ if (state.Sampler != hostSampler)
{
- _textureState[stageIndex][index].Sampler = hostSampler;
+ state.Sampler = hostSampler;
_context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler);
}
+
+ state.CachedTexture = texture;
+ state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
}
}
+
+ return specStateMatches;
}
/// <summary>
@@ -440,38 +559,72 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <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>
- private void CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex)
+ /// <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 = _imageBindingsCount[stageIndex];
if (imageCount == 0)
{
- return;
+ return true;
}
if (pool == null)
{
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses images, but texture pool was not set.");
- return;
+ return true;
}
// Scales for images appear after the texture ones.
int baseScaleIndex = _textureBindingsCount[stageIndex];
+ 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];
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
- int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
- int textureId = UnpackTextureId(packedId);
+ UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex);
- Texture texture = pool.Get(textureId);
+ int packedId = TextureHandle.ReadPackedId(bindingInfo.Handle, cachedTextureBuffer, cachedSamplerBuffer);
+ int textureId = TextureHandle.UnpackTextureId(packedId);
- ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
+ ref TextureStatePerStage 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)
+ {
+ // The texture is already bound.
+ state.CachedTexture.SynchronizeMemory();
+
+ if (isStore)
+ {
+ state.CachedTexture?.SignalModified();
+ }
+
+ 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.
@@ -494,14 +647,14 @@ namespace Ryujinx.Graphics.Gpu.Image
texture?.SignalModified();
}
- if (_imageState[stageIndex][index].Texture != hostTexture || _rebind)
+ if (state.Texture != hostTexture)
{
if (UpdateScale(texture, bindingInfo, baseScaleIndex + index, stage))
{
hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
}
- _imageState[stageIndex][index].Texture = hostTexture;
+ state.Texture = hostTexture;
Format format = bindingInfo.Format;
@@ -512,8 +665,13 @@ namespace Ryujinx.Graphics.Gpu.Image
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
}
+
+ state.CachedTexture = texture;
+ state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
}
}
+
+ return specStateMatches;
}
/// <summary>
@@ -537,7 +695,7 @@ namespace Ryujinx.Graphics.Gpu.Image
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(cbufSlot, bufferIndex);
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
- int textureId = UnpackTextureId(packedId);
+ int textureId = TextureHandle.UnpackTextureId(packedId);
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
@@ -555,6 +713,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <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);
@@ -591,31 +750,12 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
- /// Unpacks the texture ID from the real texture handle.
- /// </summary>
- /// <param name="packedId">The real texture handle</param>
- /// <returns>The texture ID</returns>
- private static int UnpackTextureId(int packedId)
- {
- return (packedId >> 0) & 0xfffff;
- }
-
- /// <summary>
- /// Unpacks the sampler ID from the real texture handle.
- /// </summary>
- /// <param name="packedId">The real texture handle</param>
- /// <returns>The sampler ID</returns>
- private static int UnpackSamplerId(int packedId)
- {
- return (packedId >> 20) & 0xfff;
- }
-
- /// <summary>
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
/// </summary>
public void Rebind()
{
- _rebind = true;
+ Array.Clear(_textureState);
+ Array.Clear(_imageState);
}
/// <summary>