mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-01-12 21:59:12 +00:00
Minor fixes, reduce amount of bindless updates, initialize table, new bindless indexed modes, etc
This commit is contained in:
parent
53440e2e21
commit
dd9134f5f5
19 changed files with 443 additions and 80 deletions
|
@ -85,6 +85,11 @@ namespace Ryujinx.Graphics.Gpu
|
|||
/// </summary>
|
||||
public const int DriverReservedUniformBuffer = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Number of the uniform buffer reserved by the driver to store texture binding handles.
|
||||
/// </summary>
|
||||
public const int DriverReserveTextureBindingsBuffer = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum size that an storage buffer is assumed to have when the correct size is unknown.
|
||||
/// </summary>
|
||||
|
|
|
@ -87,14 +87,22 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// Sets a bit to 0.
|
||||
/// </summary>
|
||||
/// <param name="bit">Index of the bit</param>
|
||||
public void Clear(int bit)
|
||||
/// <returns>True if the bit was set, false otherwise</returns>
|
||||
public bool Clear(int bit)
|
||||
{
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
ulong wordMask = 1UL << wordBit;
|
||||
|
||||
if ((_masks[wordIndex] & wordMask) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_masks[wordIndex] &= ~wordMask;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -214,6 +214,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// <param name="endAddress">End address of the region of the pool that has been modified, exclusive</param>
|
||||
protected void UpdateModifiedEntries(ulong address, ulong endAddress)
|
||||
{
|
||||
// TODO: Remove this method (it's unused now).
|
||||
|
||||
int startId = (int)((address - Address) / DescriptorSize);
|
||||
int endId = (int)((endAddress - Address + (DescriptorSize - 1)) / DescriptorSize) - 1;
|
||||
|
||||
|
@ -228,6 +230,18 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
_maximumAccessedId = Math.Max(_maximumAccessedId, endId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a entry that has been modified.
|
||||
/// </summary>
|
||||
/// <param name="id">Pool entry index</param>
|
||||
protected void UpdateModifiedEntry(int id)
|
||||
{
|
||||
ModifiedEntries.Set(id);
|
||||
|
||||
_minimumAccessedId = Math.Min(_minimumAccessedId, id);
|
||||
_maximumAccessedId = Math.Max(_maximumAccessedId, id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces all entries as modified, to be updated if any shader uses bindless textures.
|
||||
/// </summary>
|
||||
|
|
|
@ -146,6 +146,29 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampler with the specified ID, and return true or false depending on the sampler entry being modified or not since the last call.
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the texture</param>
|
||||
/// <param name="sampler">Sampler with the specified ID</param>
|
||||
/// <returns>True if the sampler entry was modified since the last call, false otherwise</returns>
|
||||
public bool TryGetBindlessSampler(int id, out Sampler sampler)
|
||||
{
|
||||
if ((uint)id < Items.Length)
|
||||
{
|
||||
if (ModifiedEntries.Clear(id))
|
||||
{
|
||||
sampler = Items[id] ?? GetValidated(id);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
sampler = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampler at the given <paramref name="id"/> from the cache,
|
||||
/// or creates a new one if not found.
|
||||
|
@ -174,8 +197,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{
|
||||
ulong endAddress = address + size;
|
||||
|
||||
UpdateModifiedEntries(address, endAddress);
|
||||
|
||||
for (; address < endAddress; address += DescriptorSize)
|
||||
{
|
||||
int id = (int)((address - Address) / DescriptorSize);
|
||||
|
@ -192,10 +213,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
continue;
|
||||
}
|
||||
|
||||
UpdateModifiedEntry(id);
|
||||
|
||||
sampler.Dispose();
|
||||
|
||||
Items[id] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateModifiedEntry(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ using Ryujinx.Graphics.Gpu.Memory;
|
|||
using Ryujinx.Graphics.Gpu.Shader;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
@ -61,6 +61,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
private int _samplerPoolSequence;
|
||||
|
||||
private BindlessTextureFlags[] _bindlessTextureFlags;
|
||||
private uint[] _bindlessIndexedBuffersMask;
|
||||
|
||||
private int _textureBufferIndex;
|
||||
|
||||
|
@ -97,6 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
_imageState = new TextureState[InitialImageStateSize];
|
||||
|
||||
_bindlessTextureFlags = new BindlessTextureFlags[stages];
|
||||
_bindlessIndexedBuffersMask = new uint[stages];
|
||||
|
||||
for (int stage = 0; stage < stages; stage++)
|
||||
{
|
||||
|
@ -115,6 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
_imageBindings = bindings.ImageBindings;
|
||||
|
||||
_bindlessTextureFlags = bindings.BindlessTextureFlags;
|
||||
_bindlessIndexedBuffersMask = bindings.BindlessIndexedBuffersMask;
|
||||
|
||||
SetMaxBindings(bindings.MaxTextureBinding, bindings.MaxImageBinding);
|
||||
}
|
||||
|
@ -366,14 +369,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
specStateMatches &= CommitTextureBindings(texturePool, samplerPool, ShaderStage.Compute, 0, poolModified, specState);
|
||||
specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
|
||||
|
||||
if (_bindlessTextureFlags[0].HasFlag(BindlessTextureFlags.BindlessNvn))
|
||||
{
|
||||
CommitBindlessResources(texturePool, ShaderStage.Compute, 0);
|
||||
}
|
||||
else if (_bindlessTextureFlags[0].HasFlag(BindlessTextureFlags.BindlessFull))
|
||||
if (_bindlessTextureFlags[0].HasFlag(BindlessTextureFlags.BindlessFull))
|
||||
{
|
||||
texturePool.LoadAll(_context.Renderer, _samplerPool);
|
||||
}
|
||||
else if ((_bindlessTextureFlags[0] & BindlessTextureFlags.BindlessNvnAny) != 0)
|
||||
{
|
||||
CommitBindlessResources(texturePool, ShaderStage.Compute, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -384,14 +387,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
specStateMatches &= CommitTextureBindings(texturePool, samplerPool, stage, stageIndex, poolModified, specState);
|
||||
specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState);
|
||||
|
||||
if (_bindlessTextureFlags[stageIndex].HasFlag(BindlessTextureFlags.BindlessNvn))
|
||||
{
|
||||
CommitBindlessResources(texturePool, stage, stageIndex);
|
||||
}
|
||||
else if (_bindlessTextureFlags[stageIndex].HasFlag(BindlessTextureFlags.BindlessFull))
|
||||
if (_bindlessTextureFlags[stageIndex].HasFlag(BindlessTextureFlags.BindlessFull))
|
||||
{
|
||||
texturePool.LoadAll(_context.Renderer, _samplerPool);
|
||||
}
|
||||
else if ((_bindlessTextureFlags[stageIndex] & BindlessTextureFlags.BindlessNvnAny) != 0)
|
||||
{
|
||||
CommitBindlessResources(texturePool, stage, stageIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -783,11 +786,50 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < 32; index++)
|
||||
{
|
||||
int wordOffset = 8 + index * 2;
|
||||
BindlessTextureFlags flags = _bindlessTextureFlags[stageIndex];
|
||||
uint buffersMask = _bindlessIndexedBuffersMask[stageIndex];
|
||||
|
||||
int packedId = ReadConstantBuffer<int>(stageIndex, _textureBufferIndex, wordOffset);
|
||||
while (buffersMask != 0)
|
||||
{
|
||||
int bufferIndex = BitOperations.TrailingZeroCount(buffersMask);
|
||||
|
||||
buffersMask &= ~(1u << bufferIndex);
|
||||
|
||||
if (bufferIndex == Constants.DriverReserveTextureBindingsBuffer)
|
||||
{
|
||||
if (flags.HasFlag(BindlessTextureFlags.BindlessNvnCombined))
|
||||
{
|
||||
CommitBindlessResourcesNvnCombined(pool, stageIndex);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(BindlessTextureFlags.BindlessNvnSeparateTexture))
|
||||
{
|
||||
CommitBindlessResourcesNvnSeparateTexture(pool, stageIndex);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(BindlessTextureFlags.BindlessNvnSeparateSampler))
|
||||
{
|
||||
CommitBindlessResourcesNvnSeparateSampler(pool, stageIndex);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong size = _isCompute
|
||||
? _channel.BufferManager.GetComputeUniformBufferSize(bufferIndex)
|
||||
: _channel.BufferManager.GetGraphicsUniformBufferSize(stageIndex, bufferIndex);
|
||||
|
||||
ReadOnlySpan<int> cbData = GetConstantBufferRange<int>(stageIndex, bufferIndex, 0, (int)(size / sizeof(int)));
|
||||
|
||||
for (int index = 0; index < cbData.Length; index += 2)
|
||||
{
|
||||
int packedId = cbData[index];
|
||||
int highWord = cbData[index + 1];
|
||||
|
||||
if (highWord != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||
int samplerId;
|
||||
|
@ -801,38 +843,115 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
samplerId = TextureHandle.UnpackSamplerId(packedId);
|
||||
}
|
||||
|
||||
Texture texture = pool.Get(textureId);
|
||||
pool.UpdateBindlessCombined(_context.Renderer, samplerPool, textureId, samplerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (texture == null)
|
||||
/// <summary>
|
||||
/// 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="stageIndex">The stage number of the specified shader stage</param>
|
||||
private void CommitBindlessResourcesNvnCombined(TexturePool pool, int stageIndex)
|
||||
{
|
||||
var samplerPool = _samplerPool;
|
||||
|
||||
ReadOnlySpan<int> cbData = GetConstantBufferRange<int>(stageIndex, Constants.DriverReserveTextureBindingsBuffer, 8, 32);
|
||||
|
||||
for (int index = 0; index < cbData.Length; index += 2)
|
||||
{
|
||||
int packedId = cbData[index];
|
||||
int highWord = cbData[index + 1];
|
||||
|
||||
if (highWord != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (texture.Target == Target.TextureBuffer)
|
||||
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||
int samplerId;
|
||||
|
||||
if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
|
||||
{
|
||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind
|
||||
// to ensure we're not using a old buffer that was already deleted.
|
||||
TextureBindingInfo bindingInfo = new(texture.Target, texture.Format, 0, 0, 0, TextureUsageFlags.None);
|
||||
ulong address = texture.Range.GetSubRange(0).Address;
|
||||
ulong size = texture.Size;
|
||||
_channel.BufferManager.SetBufferTextureStorage(texture.HostTexture, address, size, bindingInfo, texture.Format, false, textureId);
|
||||
samplerId = textureId;
|
||||
}
|
||||
else
|
||||
{
|
||||
Sampler sampler = samplerPool?.Get(samplerId);
|
||||
samplerId = TextureHandle.UnpackSamplerId(packedId);
|
||||
}
|
||||
|
||||
if (sampler == null)
|
||||
pool.UpdateBindlessCombined(_context.Renderer, samplerPool, textureId, samplerId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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="stageIndex">The stage number of the specified shader stage</param>
|
||||
private void CommitBindlessResourcesNvnSeparateTexture(TexturePool pool, int stageIndex)
|
||||
{
|
||||
ReadOnlySpan<int> cbData = GetConstantBufferRange<int>(stageIndex, Constants.DriverReserveTextureBindingsBuffer, 90, 128);
|
||||
|
||||
for (int index = 0; index < cbData.Length; index += 2)
|
||||
{
|
||||
int packedId = cbData[index];
|
||||
int highWord = cbData[index + 1];
|
||||
|
||||
if (highWord != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.RegisterBindlessTextureAndSampler(
|
||||
textureId,
|
||||
texture.HostTexture,
|
||||
texture.ScaleFactor,
|
||||
samplerId,
|
||||
sampler.GetHostSampler(texture));
|
||||
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||
|
||||
pool.UpdateBindlessCombined(_context.Renderer, null, textureId, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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="stageIndex">The stage number of the specified shader stage</param>
|
||||
private void CommitBindlessResourcesNvnSeparateSampler(TexturePool pool, int stageIndex)
|
||||
{
|
||||
var samplerPool = _samplerPool;
|
||||
if (samplerPool == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReadOnlySpan<int> cbData = GetConstantBufferRange<int>(stageIndex, Constants.DriverReserveTextureBindingsBuffer, 346, 32);
|
||||
|
||||
for (int index = 0; index < cbData.Length; index += 2)
|
||||
{
|
||||
int packedId = cbData[index];
|
||||
int highWord = cbData[index + 1];
|
||||
|
||||
if (highWord != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int samplerId;
|
||||
|
||||
if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
|
||||
{
|
||||
samplerId = TextureHandle.UnpackTextureId(packedId);
|
||||
}
|
||||
else
|
||||
{
|
||||
samplerId = TextureHandle.UnpackSamplerId(packedId);
|
||||
}
|
||||
|
||||
if (samplerPool.TryGetBindlessSampler(samplerId, out Sampler sampler))
|
||||
{
|
||||
_context.Renderer.Pipeline.RegisterBindlessSampler(samplerId, sampler.GetHostSampler(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -842,15 +961,18 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
/// <param name="stageIndex">Index of the shader stage where the constant buffer belongs</param>
|
||||
/// <param name="bufferIndex">Index of the constant buffer to read from</param>
|
||||
/// <param name="elementIndex">Index of the element on the constant buffer</param>
|
||||
/// <param name="startIndex">Index of the first element on the constant buffer</param>
|
||||
/// <param name="count">Number of elements to access</param>
|
||||
/// <returns>The value at the specified buffer and offset</returns>
|
||||
private unsafe T ReadConstantBuffer<T>(int stageIndex, int bufferIndex, int elementIndex) where T : unmanaged
|
||||
private unsafe ReadOnlySpan<T> GetConstantBufferRange<T>(int stageIndex, int bufferIndex, int startIndex, int count) where T : unmanaged
|
||||
{
|
||||
ulong baseAddress = _isCompute
|
||||
? _channel.BufferManager.GetComputeUniformBufferAddress(bufferIndex)
|
||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, bufferIndex);
|
||||
|
||||
return _channel.MemoryManager.Physical.Read<T>(baseAddress + (ulong)elementIndex * (ulong)sizeof(T));
|
||||
int typeSize = sizeof(T);
|
||||
|
||||
return MemoryMarshal.Cast<byte, T>(_channel.MemoryManager.Physical.GetSpan(baseAddress + (ulong)(startIndex * typeSize), count * typeSize));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -235,29 +235,85 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
int id;
|
||||
|
||||
while ((id = ModifiedEntries.GetNextAndClear()) >= 0)
|
||||
{
|
||||
UpdateBindlessInternal(renderer, id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the bindless texture with the given pool ID, if the pool entry has been modified since the last call.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Renderer of the current GPU context</param>
|
||||
/// <param name="samplerPool">Optional sampler pool. If null, the sampler ID is ignored</param>
|
||||
/// <param name="textureId">ID of the texture</param>
|
||||
/// <param name="samplerId">ID of the sampler</param>
|
||||
public void UpdateBindlessCombined(IRenderer renderer, SamplerPool samplerPool, int textureId, int samplerId)
|
||||
{
|
||||
if ((uint)textureId >= Items.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Items[textureId]?.SynchronizeMemory();
|
||||
|
||||
bool textureModified = ModifiedEntries.Clear(textureId);
|
||||
|
||||
if (samplerPool != null)
|
||||
{
|
||||
bool samplerModified = samplerPool.TryGetBindlessSampler(samplerId, out Sampler sampler);
|
||||
|
||||
if (sampler == null)
|
||||
{
|
||||
if (textureModified)
|
||||
{
|
||||
UpdateBindlessInternal(renderer, textureId);
|
||||
}
|
||||
}
|
||||
else if (textureModified || samplerModified)
|
||||
{
|
||||
Texture texture = Items[textureId] ?? GetValidated(textureId);
|
||||
|
||||
if (texture != null)
|
||||
{
|
||||
if (texture.Target != Target.TextureBuffer)
|
||||
{
|
||||
renderer.Pipeline.RegisterBindlessTextureAndSampler(
|
||||
textureId,
|
||||
texture.HostTexture,
|
||||
texture.ScaleFactor,
|
||||
samplerId,
|
||||
sampler.GetHostSampler(null));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.Pipeline.RegisterBindlessSampler(samplerId, sampler.GetHostSampler(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (textureModified)
|
||||
{
|
||||
UpdateBindlessInternal(renderer, textureId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the bindless texture with the given pool ID.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Renderer of the current GPU context</param>
|
||||
/// <param name="id">ID of the texture</param>
|
||||
private void UpdateBindlessInternal(IRenderer renderer, int id)
|
||||
{
|
||||
Texture texture = Items[id] ?? GetValidated(id);
|
||||
|
||||
if (texture != null)
|
||||
{
|
||||
if (texture.Target == Target.TextureBuffer)
|
||||
{
|
||||
_channel.BufferManager.SetBufferTextureStorage(
|
||||
texture.HostTexture,
|
||||
texture.Range.GetSubRange(0).Address,
|
||||
texture.Size,
|
||||
default,
|
||||
0,
|
||||
false,
|
||||
id);
|
||||
}
|
||||
else
|
||||
if (texture.Target != Target.TextureBuffer)
|
||||
{
|
||||
renderer.Pipeline.RegisterBindlessTexture(id, texture.HostTexture, texture.ScaleFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture at the given <paramref name="id"/> from the cache,
|
||||
|
@ -431,8 +487,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
ulong endAddress = address + size;
|
||||
|
||||
UpdateModifiedEntries(address, endAddress);
|
||||
|
||||
for (; address < endAddress; address += DescriptorSize)
|
||||
{
|
||||
int id = (int)((address - Address) / DescriptorSize);
|
||||
|
@ -451,6 +505,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
continue;
|
||||
}
|
||||
|
||||
UpdateModifiedEntry(id);
|
||||
|
||||
if (texture.HasOneReference())
|
||||
{
|
||||
_channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
|
||||
|
@ -461,6 +517,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
texture.DecrementReferenceCount(this, id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateModifiedEntry(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -429,6 +429,27 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
return _gpUniformBuffers[stage].Buffers[index].Address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the compute uniform buffer currently bound at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the uniform buffer binding</param>
|
||||
/// <returns>The uniform buffer size, or an undefined value if the buffer is not currently bound</returns>
|
||||
public ulong GetComputeUniformBufferSize(int index)
|
||||
{
|
||||
return _cpUniformBuffers.Buffers[index].Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the graphics uniform buffer currently bound at the given index.
|
||||
/// </summary>
|
||||
/// <param name="stage">Index of the shader stage</param>
|
||||
/// <param name="index">Index of the uniform buffer binding</param>
|
||||
/// <returns>The uniform buffer size, or an undefined value if the buffer is not currently bound</returns>
|
||||
public ulong GetGraphicsUniformBufferSize(int stage, int index)
|
||||
{
|
||||
return _gpUniformBuffers[stage].Buffers[index].Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounds of the uniform buffer currently bound at the given index.
|
||||
/// </summary>
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
public BufferDescriptor[][] StorageBufferBindings { get; }
|
||||
|
||||
public BindlessTextureFlags[] BindlessTextureFlags { get; }
|
||||
public uint[] BindlessIndexedBuffersMask { get; }
|
||||
|
||||
public int MaxTextureBinding { get; }
|
||||
public int MaxImageBinding { get; }
|
||||
|
@ -37,6 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
StorageBufferBindings = new BufferDescriptor[stageCount][];
|
||||
|
||||
BindlessTextureFlags = new BindlessTextureFlags[stageCount];
|
||||
BindlessIndexedBuffersMask = new uint[stageCount];
|
||||
|
||||
int maxTextureBinding = -1;
|
||||
int maxImageBinding = -1;
|
||||
|
@ -100,6 +102,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
StorageBufferBindings[i] = stage.Info.SBuffers.ToArray();
|
||||
|
||||
BindlessTextureFlags[i] = stage.Info.BindlessTextureFlags;
|
||||
BindlessIndexedBuffersMask[i] = stage.Info.BindlessIndexedBuffersMask;
|
||||
}
|
||||
|
||||
MaxTextureBinding = maxTextureBinding;
|
||||
|
|
|
@ -189,6 +189,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
/// Flags indicating if and how bindless texture accesses were translated for the shader stage.
|
||||
/// </summary>
|
||||
public BindlessTextureFlags BindlessTextureFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Bit mask indicating which constant buffers are accessed on the shader using indexing to load texture handles.
|
||||
/// </summary>
|
||||
public uint BindlessIndexedBuffersMask;
|
||||
}
|
||||
|
||||
private readonly DiskCacheGuestStorage _guestStorage;
|
||||
|
@ -805,6 +810,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
images,
|
||||
dataInfo.Stage,
|
||||
dataInfo.BindlessTextureFlags,
|
||||
dataInfo.BindlessIndexedBuffersMask,
|
||||
dataInfo.GeometryVerticesPerPrimitive,
|
||||
dataInfo.GeometryMaxOutputVertices,
|
||||
dataInfo.ThreadsPerInputPrimitive,
|
||||
|
@ -836,6 +842,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
ImagesCount = (ushort)info.Images.Count,
|
||||
Stage = info.Stage,
|
||||
BindlessTextureFlags = info.BindlessTextureFlags,
|
||||
BindlessIndexedBuffersMask = info.BindlessIndexedBuffersMask,
|
||||
GeometryVerticesPerPrimitive = (byte)info.GeometryVerticesPerPrimitive,
|
||||
GeometryMaxOutputVertices = (ushort)info.GeometryMaxOutputVertices,
|
||||
ThreadsPerInputPrimitive = (ushort)info.ThreadsPerInputPrimitive,
|
||||
|
|
|
@ -5,7 +5,10 @@ namespace Ryujinx.Graphics.Shader
|
|||
None = 0,
|
||||
|
||||
BindlessConverted = 1 << 0,
|
||||
BindlessNvn = 1 << 1,
|
||||
BindlessFull = 1 << 2,
|
||||
BindlessNvnCombined = 1 << 1,
|
||||
BindlessNvnSeparateTexture = 1 << 2,
|
||||
BindlessNvnSeparateSampler = 1 << 3,
|
||||
BindlessFull = 1 << 4,
|
||||
BindlessNvnAny = BindlessNvnCombined | BindlessNvnSeparateTexture | BindlessNvnSeparateSampler,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
public static void Declare(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
context.AppendLine(context.TargetApi == TargetApi.Vulkan ? "#version 460 core" : "#version 450 core");
|
||||
context.AppendLine("#extension GL_ARB_bindless_texture : enable");
|
||||
context.AppendLine("#extension GL_ARB_gpu_shader_int64 : enable");
|
||||
|
||||
if (context.HostCapabilities.SupportsShaderBallot)
|
||||
|
@ -36,6 +35,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
context.AppendLine("#extension GL_EXT_nonuniform_qualifier : enable");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AppendLine("#extension GL_ARB_bindless_texture : enable");
|
||||
}
|
||||
|
||||
if (context.Definitions.Stage == ShaderStage.Compute)
|
||||
{
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
uint Helper_GetBindlessTextureIndex(int nvHandle)
|
||||
{
|
||||
int id = nvHandle & 0xfffff;
|
||||
return bindless_table[id >> 8].x | uint(id & 0xff);
|
||||
return bindless_table.table[id >> 8].x | uint(id & 0xff);
|
||||
}
|
||||
|
||||
uint Helper_GetBindlessSamplerIndex(int nvHandle)
|
||||
{
|
||||
int id = (nvHandle >> 20) & 0xfff;
|
||||
return bindless_table[id >> 8].y | uint(id & 0xff);
|
||||
return bindless_table.table[id >> 8].y | uint(id & 0xff);
|
||||
}
|
||||
|
||||
float Helper_GetBindlessScale(int nvHandle)
|
||||
{
|
||||
return bindless_scales[Helper_GetBindlessTextureIndex(nvHandle)];
|
||||
return bindless_scales.scales[Helper_GetBindlessTextureIndex(nvHandle)];
|
||||
}
|
|
@ -11,6 +11,12 @@ namespace Ryujinx.Graphics.Shader
|
|||
public const int NvnBaseInstanceByteOffset = 0x644;
|
||||
public const int NvnDrawIndexByteOffset = 0x648;
|
||||
|
||||
public const int NvnTextureCbSlot = 2;
|
||||
public const int NvnSeparateTextureBindingsStartByteOffset = 0x168;
|
||||
public const int NvnSeparateTextureBindingsEndByteOffset = 0x568;
|
||||
public const int NvnSeparateSamplerBindingsStartByteOffset = 0x568;
|
||||
public const int NvnSeparateSamplerBindingsEndByteOffset = 0x668;
|
||||
|
||||
public const int VkConstantBufferSetIndex = 0;
|
||||
public const int VkStorageBufferSetIndex = 1;
|
||||
public const int VkTextureSetIndex = 2;
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
|
||||
public ShaderStage Stage { get; }
|
||||
public BindlessTextureFlags BindlessTextureFlags { get; }
|
||||
public uint BindlessIndexedBuffersMask { get; }
|
||||
public int GeometryVerticesPerPrimitive { get; }
|
||||
public int GeometryMaxOutputVertices { get; }
|
||||
public int ThreadsPerInputPrimitive { get; }
|
||||
|
@ -29,6 +30,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
TextureDescriptor[] images,
|
||||
ShaderStage stage,
|
||||
BindlessTextureFlags bindlessTextureFlags,
|
||||
uint bindlessIndexedBuffersMask,
|
||||
int geometryVerticesPerPrimitive,
|
||||
int geometryMaxOutputVertices,
|
||||
int threadsPerInputPrimitive,
|
||||
|
@ -46,6 +48,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
|
||||
Stage = stage;
|
||||
BindlessTextureFlags = bindlessTextureFlags;
|
||||
BindlessIndexedBuffersMask = bindlessIndexedBuffersMask;
|
||||
GeometryVerticesPerPrimitive = geometryVerticesPerPrimitive;
|
||||
GeometryMaxOutputVertices = geometryMaxOutputVertices;
|
||||
ThreadsPerInputPrimitive = threadsPerInputPrimitive;
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
public readonly ShaderStage Stage;
|
||||
public readonly ref FeatureFlags UsedFeatures;
|
||||
public readonly ref BindlessTextureFlags BindlessTextureFlags;
|
||||
public readonly ref uint BindlessIndexedBuffersMask;
|
||||
public readonly ref bool BindlessTexturesAllowed;
|
||||
|
||||
public TransformContext(
|
||||
|
@ -27,6 +28,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
ShaderStage stage,
|
||||
ref FeatureFlags usedFeatures,
|
||||
ref BindlessTextureFlags bindlessTextureFlags,
|
||||
ref uint bindlessIndexedBuffersMask,
|
||||
ref bool bindlessTexturesAllowed)
|
||||
{
|
||||
Hfm = hfm;
|
||||
|
@ -39,6 +41,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
Stage = stage;
|
||||
UsedFeatures = ref usedFeatures;
|
||||
BindlessTextureFlags = ref bindlessTextureFlags;
|
||||
BindlessIndexedBuffersMask = ref bindlessIndexedBuffersMask;
|
||||
BindlessTexturesAllowed = ref bindlessTexturesAllowed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
context.ResourceManager,
|
||||
context.TargetApi,
|
||||
ref context.BindlessTextureFlags,
|
||||
ref context.BindlessIndexedBuffersMask,
|
||||
context.BindlessTexturesAllowed,
|
||||
context.GpuAccessor.QueryTextureBufferIndex());
|
||||
|
||||
|
@ -784,6 +785,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
ResourceManager resourceManager,
|
||||
TargetApi targetApi,
|
||||
ref BindlessTextureFlags bindlessTextureFlags,
|
||||
ref uint bindlessIndexedBuffersMask,
|
||||
bool bindlessTexturesAllowed,
|
||||
int textureBufferIndex)
|
||||
{
|
||||
|
@ -797,9 +799,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
{
|
||||
resourceManager.EnsureBindlessBinding(targetApi, texOp.Type, texOp.Inst.IsImage());
|
||||
|
||||
if (IsIndexedAccess(resourceManager, texOp, textureBufferIndex))
|
||||
if (IsIndexedAccess(resourceManager, texOp, ref bindlessTextureFlags, ref bindlessIndexedBuffersMask, textureBufferIndex))
|
||||
{
|
||||
bindlessTextureFlags |= BindlessTextureFlags.BindlessNvn;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -865,7 +866,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
return node;
|
||||
}
|
||||
|
||||
private static bool IsIndexedAccess(ResourceManager resourceManager, TextureOperation texOp, int textureBufferIndex)
|
||||
private static bool IsIndexedAccess(
|
||||
ResourceManager resourceManager,
|
||||
TextureOperation texOp,
|
||||
ref BindlessTextureFlags bindlessTextureFlags,
|
||||
ref uint bindlessIndexedBuffersMask,
|
||||
int textureBufferIndex)
|
||||
{
|
||||
// Try to detect a indexed access.
|
||||
// The access is considered indexed if the handle is loaded with a LDC instruction
|
||||
|
@ -875,6 +881,70 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
return false;
|
||||
}
|
||||
|
||||
if (handleAsgOp.Inst == Instruction.BitwiseOr)
|
||||
{
|
||||
if (IsCbLoadOrCb(resourceManager, handleAsgOp.GetSource(0), ref bindlessTextureFlags, out int ldc0CbSlot, textureBufferIndex) &&
|
||||
IsCbLoadOrCb(resourceManager, handleAsgOp.GetSource(1), ref bindlessTextureFlags, out int ldc1CbSlot, textureBufferIndex))
|
||||
{
|
||||
bindlessIndexedBuffersMask |= (1u << ldc0CbSlot) | (1u << ldc1CbSlot);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (IsCbLoad(resourceManager, handleAsgOp, out int cbSlot))
|
||||
{
|
||||
bindlessTextureFlags |= BindlessTextureFlags.BindlessNvnCombined;
|
||||
bindlessIndexedBuffersMask |= 1u << cbSlot;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsCbLoadOrCb(
|
||||
ResourceManager resourceManager,
|
||||
Operand operand,
|
||||
ref BindlessTextureFlags bindlessTextureFlags,
|
||||
out int cbSlot,
|
||||
int textureBufferIndex)
|
||||
{
|
||||
cbSlot = 0;
|
||||
|
||||
if (operand.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
cbSlot = operand.GetCbufSlot();
|
||||
|
||||
if (cbSlot == textureBufferIndex && textureBufferIndex == Constants.NvnTextureCbSlot)
|
||||
{
|
||||
int cbOffset = operand.GetCbufOffset();
|
||||
|
||||
if (cbOffset >= Constants.NvnSeparateTextureBindingsStartByteOffset / 4 &&
|
||||
cbOffset < Constants.NvnSeparateTextureBindingsEndByteOffset / 4)
|
||||
{
|
||||
bindlessTextureFlags |= BindlessTextureFlags.BindlessNvnSeparateTexture;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (cbOffset >= Constants.NvnSeparateSamplerBindingsStartByteOffset / 4 &&
|
||||
cbOffset < Constants.NvnSeparateSamplerBindingsEndByteOffset / 4)
|
||||
{
|
||||
bindlessTextureFlags |= BindlessTextureFlags.BindlessNvnSeparateSampler;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return operand.AsgOp is Operation operation && IsCbLoad(resourceManager, operation, out cbSlot);
|
||||
}
|
||||
|
||||
private static bool IsCbLoad(ResourceManager resourceManager, Operation handleAsgOp, out int cbSlot)
|
||||
{
|
||||
cbSlot = 0;
|
||||
|
||||
if (handleAsgOp.Inst != Instruction.Load || handleAsgOp.StorageKind != StorageKind.ConstantBuffer)
|
||||
{
|
||||
return false;
|
||||
|
@ -883,8 +953,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||
Operand ldcSrc0 = handleAsgOp.GetSource(0);
|
||||
|
||||
return ldcSrc0.Type == OperandType.Constant &&
|
||||
resourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int cbSlot) &&
|
||||
cbSlot == textureBufferIndex;
|
||||
resourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out cbSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,6 +265,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
HelperFunctionManager hfm = new(funcs, Definitions.Stage);
|
||||
|
||||
BindlessTextureFlags bindlessTextureFlags = BindlessTextureFlags.None;
|
||||
uint bindlessIndexedBuffersMask = 0;
|
||||
bool bindlessTexturesAllowed = true;
|
||||
|
||||
for (int i = 0; i < functions.Length; i++)
|
||||
|
@ -302,6 +303,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
Definitions.Stage,
|
||||
ref usedFeatures,
|
||||
ref bindlessTextureFlags,
|
||||
ref bindlessIndexedBuffersMask,
|
||||
ref bindlessTexturesAllowed);
|
||||
|
||||
Optimizer.RunPass(context);
|
||||
|
@ -319,6 +321,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
resourceManager,
|
||||
usedFeatures,
|
||||
bindlessTextureFlags,
|
||||
bindlessIndexedBuffersMask,
|
||||
clipDistancesWritten);
|
||||
}
|
||||
|
||||
|
@ -330,6 +333,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
ResourceManager resourceManager,
|
||||
FeatureFlags usedFeatures,
|
||||
BindlessTextureFlags bindlessTextureFlags,
|
||||
uint bindlessIndexedBuffersMask,
|
||||
byte clipDistancesWritten)
|
||||
{
|
||||
var sInfo = StructuredProgram.MakeStructuredProgram(
|
||||
|
@ -354,6 +358,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
resourceManager.GetImageDescriptors(),
|
||||
originalDefinitions.Stage,
|
||||
bindlessTextureFlags,
|
||||
bindlessIndexedBuffersMask,
|
||||
geometryVerticesPerPrimitive,
|
||||
originalDefinitions.MaxOutputVertices,
|
||||
originalDefinitions.ThreadsPerInputPrimitive,
|
||||
|
@ -586,6 +591,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
resourceManager,
|
||||
FeatureFlags.None,
|
||||
BindlessTextureFlags.None,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
||||
|
@ -683,6 +689,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
resourceManager,
|
||||
FeatureFlags.RtLayer,
|
||||
BindlessTextureFlags.None,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
@ -270,16 +271,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
bbiDsc.UpdateBufferImage(0, 0, i, bufferView, DescriptorType.StorageTexelBuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
btDsc.UpdateImage(0, 2, i, default, DescriptorType.SampledImage);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _samplerRefs.Length; i++)
|
||||
{
|
||||
var sampler = _samplerRefs[i];
|
||||
if (sampler != null)
|
||||
{
|
||||
var sd = new DescriptorImageInfo()
|
||||
{
|
||||
Sampler = sampler.Get(cbs).Value
|
||||
Sampler = sampler?.Get(cbs).Value ?? default
|
||||
};
|
||||
|
||||
if (sd.Sampler.Handle == 0)
|
||||
|
@ -289,7 +292,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
bsDsc.UpdateImage(0, 0, i, sd, DescriptorType.Sampler);
|
||||
}
|
||||
}
|
||||
|
||||
_pipelineLayout = plce.PipelineLayout;
|
||||
_bindlessTextures = btDsc;
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
// All bindless resources have the update after bind flag set,
|
||||
// this is required for Intel, otherwise it just crashes when binding the descriptor sets
|
||||
// for bindless resources (maybe because it is above the limit?)
|
||||
bool updateAfterBind = setIndex >= PipelineBase.BindlessBufferTextureSetIndex;
|
||||
bool updateAfterBind = setIndex >= PipelineBase.BindlessTexturesSetIndex;
|
||||
|
||||
var dsc = _gd.DescriptorSetManager.AllocateDescriptorSet(
|
||||
_gd.Api,
|
||||
|
|
Loading…
Reference in a new issue