Minor fixes, reduce amount of bindless updates, initialize table, new bindless indexed modes, etc

This commit is contained in:
Gabriel A 2023-11-12 21:19:57 -03:00
parent 53440e2e21
commit dd9134f5f5
19 changed files with 443 additions and 80 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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);
}
}
}

View file

@ -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,89 @@ 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;
if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
{
samplerId = textureId;
}
else
{
samplerId = TextureHandle.UnpackSamplerId(packedId);
}
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 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;
}
int textureId = TextureHandle.UnpackTextureId(packedId);
int samplerId;
@ -801,38 +882,76 @@ 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 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;
}
if (texture.Target == Target.TextureBuffer)
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)
{
// 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);
continue;
}
int samplerId;
if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
{
samplerId = TextureHandle.UnpackTextureId(packedId);
}
else
{
Sampler sampler = samplerPool?.Get(samplerId);
samplerId = TextureHandle.UnpackSamplerId(packedId);
}
if (sampler == null)
{
continue;
}
_context.Renderer.Pipeline.RegisterBindlessTextureAndSampler(
textureId,
texture.HostTexture,
texture.ScaleFactor,
samplerId,
sampler.GetHostSampler(texture));
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>

View file

@ -236,27 +236,83 @@ namespace Ryujinx.Graphics.Gpu.Image
while ((id = ModifiedEntries.GetNextAndClear()) >= 0)
{
Texture texture = Items[id] ?? GetValidated(id);
UpdateBindlessInternal(renderer, id);
}
}
if (texture != null)
/// <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 (texture.Target == Target.TextureBuffer)
if (textureModified)
{
_channel.BufferManager.SetBufferTextureStorage(
texture.HostTexture,
texture.Range.GetSubRange(0).Address,
texture.Size,
default,
0,
false,
id);
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.RegisterBindlessTexture(id, texture.HostTexture, texture.ScaleFactor);
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)
{
renderer.Pipeline.RegisterBindlessTexture(id, texture.HostTexture, texture.ScaleFactor);
}
}
}
/// <summary>
@ -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);
}
}
}

View file

@ -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>

View file

@ -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;

View file

@ -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,

View file

@ -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,
}
}

View file

@ -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)
{

View file

@ -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)];
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}

View file

@ -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,25 +271,26 @@ 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()
{
var sd = new DescriptorImageInfo()
{
Sampler = sampler.Get(cbs).Value
};
Sampler = sampler?.Get(cbs).Value ?? default
};
if (sd.Sampler.Handle == 0)
{
sd.Sampler = dummySampler.GetSampler().Get(cbs).Value;
}
bsDsc.UpdateImage(0, 0, i, sd, DescriptorType.Sampler);
if (sd.Sampler.Handle == 0)
{
sd.Sampler = dummySampler.GetSampler().Get(cbs).Value;
}
bsDsc.UpdateImage(0, 0, i, sd, DescriptorType.Sampler);
}
_pipelineLayout = plce.PipelineLayout;

View file

@ -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,