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:
parent
9b94662b4b
commit
c6f8bfed90
39 changed files with 1091 additions and 311 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue