Optimize Texture Binding and Shader Specialization Checks (#3399)
* Changes 1 * Changes 2 * Better ModifiedSequence handling This should handle PreciseEvents properly, and simplifies a few things. * Minor changes, remove debug log * Handle stage.Info being null Hopefully fixes Catherine crash * Fix shader specialization fast texture lookup * Fix some things. * Address Feedback Part 1 * Make method static.
This commit is contained in:
parent
d987cacfb7
commit
99ffc061d3
15 changed files with 766 additions and 180 deletions
|
@ -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,15 +125,6 @@ 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];
|
||||
|
||||
for (int i = 0; i < toClear; i++)
|
||||
{
|
||||
state[i] = new TextureStatePerStage();
|
||||
}
|
||||
|
||||
_imageBindingsCount[stage] = count;
|
||||
|
@ -142,6 +132,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
return _imageBindings[stage];
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
@ -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);
|
||||
|
@ -590,32 +749,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
return handle;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue