mirror of
https://github.com/Ryubing/Ryujinx.git
synced 2025-11-22 12:20:54 -05:00
Add support for bindless textures from shader input (vertex buffer) on Vulkan (#6577)
* Add support for bindless textures from shader input (vertex buffer) * Shader cache version bump * Format whitespace * Remove cache entries on pool removal, disable for OpenGL * PR feedback
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
@@ -31,7 +32,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp))
|
||||
if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp) &&
|
||||
!GenerateBindlessAccess(block, resourceManager, gpuAccessor, texOp, node))
|
||||
{
|
||||
// If we can't do bindless elimination, remove the texture operation.
|
||||
// Set any destination variables to zero.
|
||||
@@ -46,6 +48,88 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GenerateBindlessAccess(
|
||||
BasicBlock block,
|
||||
ResourceManager resourceManager,
|
||||
IGpuAccessor gpuAccessor,
|
||||
TextureOperation texOp,
|
||||
LinkedListNode<INode> node)
|
||||
{
|
||||
if (!gpuAccessor.QueryHostSupportsSeparateSampler())
|
||||
{
|
||||
// We depend on combining samplers and textures in the shader being supported for this.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Operand nvHandle = texOp.GetSource(0);
|
||||
|
||||
if (nvHandle.AsgOp is not Operation handleOp ||
|
||||
handleOp.Inst != Instruction.Load ||
|
||||
handleOp.StorageKind != StorageKind.Input)
|
||||
{
|
||||
// Right now, we only allow bindless access when the handle comes from a shader input.
|
||||
// This is an artificial limitation to prevent it from being used in cases where it
|
||||
// would have a large performance impact of loading all textures in the pool.
|
||||
// It might be removed in the future, if we can mitigate the performance impact.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Operand textureHandle = OperandHelper.Local();
|
||||
Operand samplerHandle = OperandHelper.Local();
|
||||
Operand textureIndex = OperandHelper.Local();
|
||||
|
||||
block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, nvHandle, OperandHelper.Const(0xfffff)));
|
||||
block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, nvHandle, OperandHelper.Const(20)));
|
||||
|
||||
int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool());
|
||||
|
||||
block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, textureIndex, textureHandle, OperandHelper.Const(texturePoolLength - 1)));
|
||||
|
||||
texOp.SetSource(0, textureIndex);
|
||||
|
||||
bool hasSampler = !texOp.Inst.IsImage();
|
||||
|
||||
int textureBinding = resourceManager.GetTextureOrImageBinding(
|
||||
texOp.Inst,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags & ~TextureFlags.Bindless,
|
||||
0,
|
||||
TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
|
||||
texturePoolLength,
|
||||
hasSampler);
|
||||
|
||||
if (hasSampler)
|
||||
{
|
||||
Operand samplerIndex = OperandHelper.Local();
|
||||
|
||||
int samplerPoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QuerySamplerArrayLengthFromPool());
|
||||
|
||||
block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, samplerIndex, samplerHandle, OperandHelper.Const(samplerPoolLength - 1)));
|
||||
|
||||
texOp.InsertSource(1, samplerIndex);
|
||||
|
||||
int samplerBinding = resourceManager.GetTextureOrImageBinding(
|
||||
texOp.Inst,
|
||||
SamplerType.None,
|
||||
texOp.Format,
|
||||
TextureFlags.None,
|
||||
0,
|
||||
TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
|
||||
samplerPoolLength);
|
||||
|
||||
texOp.TurnIntoArray(textureBinding, samplerBinding);
|
||||
}
|
||||
else
|
||||
{
|
||||
texOp.TurnIntoArray(textureBinding);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp)
|
||||
{
|
||||
if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
private const int HardcodedArrayLengthOgl = 4;
|
||||
|
||||
// 1 and 0 elements are not considered arrays anymore.
|
||||
private const int MinimumArrayLength = 2;
|
||||
public const int MinimumArrayLength = 2;
|
||||
|
||||
public static void RunPassOgl(BasicBlock block, ResourceManager resourceManager)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
private readonly HashSet<int> _usedConstantBufferBindings;
|
||||
|
||||
private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, SamplerType Type, TextureFormat Format);
|
||||
private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, bool Separate, SamplerType Type, TextureFormat Format);
|
||||
|
||||
private struct TextureMeta
|
||||
{
|
||||
@@ -225,7 +225,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
TextureFlags flags,
|
||||
int cbufSlot,
|
||||
int handle,
|
||||
int arrayLength = 1)
|
||||
int arrayLength = 1,
|
||||
bool separate = false)
|
||||
{
|
||||
inst &= Instruction.Mask;
|
||||
bool isImage = inst.IsImage();
|
||||
@@ -239,7 +240,18 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
format = TextureFormat.Unknown;
|
||||
}
|
||||
|
||||
int binding = GetTextureOrImageBinding(cbufSlot, handle, arrayLength, type, format, isImage, intCoords, isWrite, accurateType, coherent);
|
||||
int binding = GetTextureOrImageBinding(
|
||||
cbufSlot,
|
||||
handle,
|
||||
arrayLength,
|
||||
type,
|
||||
format,
|
||||
isImage,
|
||||
intCoords,
|
||||
isWrite,
|
||||
accurateType,
|
||||
coherent,
|
||||
separate);
|
||||
|
||||
_gpuAccessor.RegisterTexture(handle, cbufSlot);
|
||||
|
||||
@@ -256,9 +268,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
bool intCoords,
|
||||
bool write,
|
||||
bool accurateType,
|
||||
bool coherent)
|
||||
bool coherent,
|
||||
bool separate)
|
||||
{
|
||||
var dimensions = type.GetDimensions();
|
||||
var dimensions = type == SamplerType.None ? 0 : type.GetDimensions();
|
||||
var dict = isImage ? _usedImages : _usedTextures;
|
||||
|
||||
var usageFlags = TextureUsageFlags.None;
|
||||
@@ -290,7 +303,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
// For array textures, we also want to use type as key,
|
||||
// since we may have texture handles stores in the same buffer, but for textures with different types.
|
||||
var keyType = arrayLength > 1 ? type : SamplerType.None;
|
||||
var info = new TextureInfo(cbufSlot, handle, arrayLength, keyType, format);
|
||||
var info = new TextureInfo(cbufSlot, handle, arrayLength, separate, keyType, format);
|
||||
var meta = new TextureMeta()
|
||||
{
|
||||
AccurateType = accurateType,
|
||||
@@ -332,6 +345,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}"
|
||||
: $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
|
||||
}
|
||||
else if (type == SamplerType.None)
|
||||
{
|
||||
nameSuffix = cbufSlot < 0 ? $"s_tcb_{handle:X}" : $"s_cb{cbufSlot}_{handle:X}";
|
||||
}
|
||||
else
|
||||
{
|
||||
nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}";
|
||||
@@ -341,6 +358,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
isImage ? 3 : 2,
|
||||
binding,
|
||||
arrayLength,
|
||||
separate,
|
||||
$"{_stagePrefix}_{nameSuffix}",
|
||||
meta.Type,
|
||||
info.Format,
|
||||
@@ -495,6 +513,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
info.CbufSlot,
|
||||
info.Handle,
|
||||
info.ArrayLength,
|
||||
info.Separate,
|
||||
meta.UsageFlags));
|
||||
}
|
||||
|
||||
@@ -514,6 +533,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
info.CbufSlot,
|
||||
info.Handle,
|
||||
info.ArrayLength,
|
||||
info.Separate,
|
||||
meta.UsageFlags));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
if (Stage == ShaderStage.Vertex)
|
||||
{
|
||||
int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding;
|
||||
TextureDefinition indexBuffer = new(2, ibBinding, 1, "ib_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
|
||||
TextureDefinition indexBuffer = new(2, ibBinding, "ib_data", SamplerType.TextureBuffer);
|
||||
resourceManager.Properties.AddOrUpdateTexture(indexBuffer);
|
||||
|
||||
int inputMap = _program.AttributeUsage.UsedInputAttributes;
|
||||
@@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
int location = BitOperations.TrailingZeroCount(inputMap);
|
||||
int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location);
|
||||
TextureDefinition vaBuffer = new(2, binding, 1, $"vb_data{location}", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
|
||||
TextureDefinition vaBuffer = new(2, binding, $"vb_data{location}", SamplerType.TextureBuffer);
|
||||
resourceManager.Properties.AddOrUpdateTexture(vaBuffer);
|
||||
|
||||
inputMap &= ~(1 << location);
|
||||
@@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
else if (Stage == ShaderStage.Geometry)
|
||||
{
|
||||
int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding;
|
||||
TextureDefinition remapBuffer = new(2, trbBinding, 1, "trb_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None);
|
||||
TextureDefinition remapBuffer = new(2, trbBinding, "trb_data", SamplerType.TextureBuffer);
|
||||
resourceManager.Properties.AddOrUpdateTexture(remapBuffer);
|
||||
|
||||
int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;
|
||||
|
||||
Reference in New Issue
Block a user