Add partial support for array of samplers, and add pass to identify them from bindless texture accesses

This commit is contained in:
gdk 2019-11-02 23:07:21 -03:00 committed by Thog
parent 63345a3098
commit 3ab5c23f49
10 changed files with 210 additions and 39 deletions

View file

@ -670,7 +670,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
private static Target GetTarget(SamplerType type) private static Target GetTarget(SamplerType type)
{ {
type &= ~SamplerType.Shadow; type &= ~(SamplerType.Indexed | SamplerType.Shadow);
switch (type) switch (type)
{ {

View file

@ -231,7 +231,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
foreach (AstTextureOperation texOp in info.Samplers.OrderBy(x => x.Handle)) foreach (AstTextureOperation texOp in info.Samplers.OrderBy(x => x.Handle))
{ {
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp); string indexExpr = NumberFormatter.FormatInt(texOp.ArraySize);
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
if (!samplers.TryAdd(samplerName, texOp)) if (!samplers.TryAdd(samplerName, texOp))
{ {
@ -257,14 +259,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
desc = new TextureDescriptor(samplerName, texOp.Type, operand.CbufSlot, operand.CbufOffset); desc = new TextureDescriptor(samplerName, texOp.Type, operand.CbufSlot, operand.CbufOffset);
} }
else if ((texOp.Type & SamplerType.Indexed) != 0)
{
for (int index = 0; index < texOp.ArraySize; index++)
{
string indexExpr = NumberFormatter.FormatInt(index);
string indexedSamplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
desc = new TextureDescriptor(indexedSamplerName, texOp.Type, texOp.Handle + index * 2);
context.TextureDescriptors.Add(desc);
}
}
else else
{ {
desc = new TextureDescriptor(samplerName, texOp.Type, texOp.Handle); desc = new TextureDescriptor(samplerName, texOp.Type, texOp.Handle);
}
context.TextureDescriptors.Add(desc); context.TextureDescriptors.Add(desc);
} }
} }
}
private static void DeclareImages(CodeGenContext context, StructuredProgramInfo info) private static void DeclareImages(CodeGenContext context, StructuredProgramInfo info)
{ {
@ -272,7 +287,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
foreach (AstTextureOperation texOp in info.Images.OrderBy(x => x.Handle)) foreach (AstTextureOperation texOp in info.Images.OrderBy(x => x.Handle))
{ {
string imageName = OperandManager.GetImageName(context.Config.Stage, texOp); string indexExpr = NumberFormatter.FormatInt(texOp.ArraySize);
string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr);
if (!images.TryAdd(imageName, texOp)) if (!images.TryAdd(imageName, texOp))
{ {
@ -290,11 +307,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AstTextureOperation texOp = kv.Value; AstTextureOperation texOp = kv.Value;
TextureDescriptor desc = new TextureDescriptor(imageName, texOp.Type, texOp.Handle); if ((texOp.Type & SamplerType.Indexed) != 0)
{
for (int index = 0; index < texOp.ArraySize; index++)
{
string indexExpr = NumberFormatter.FormatInt(index);
string indexedSamplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
var desc = new TextureDescriptor(indexedSamplerName, texOp.Type, texOp.Handle + index * 2);
context.TextureDescriptors.Add(desc);
}
}
else
{
var desc = new TextureDescriptor(imageName, texOp.Type, texOp.Handle);
context.ImageDescriptors.Add(desc); context.ImageDescriptors.Add(desc);
} }
} }
}
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info) private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info)
{ {

View file

@ -16,10 +16,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isArray = (texOp.Type & SamplerType.Array) != 0;
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
string texCall = "imageStore"; string texCall = "imageStore";
string imageName = OperandManager.GetImageName(context.Config.Stage, texOp); int srcIndex = isBindless ? 1 : 0;
string Src(VariableType type)
{
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
}
string indexExpr = null;
if (isIndexed)
{
indexExpr = Src(VariableType.S32);
}
string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr);
texCall += "(" + imageName; texCall += "(" + imageName;
@ -34,13 +49,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
arrayIndexElem = pCount++; arrayIndexElem = pCount++;
} }
int srcIndex = isBindless ? 1 : 0;
string Src(VariableType type)
{
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
}
void Append(string str) void Append(string str)
{ {
texCall += ", " + str; texCall += ", " + str;
@ -174,6 +182,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isArray = (texOp.Type & SamplerType.Array) != 0;
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0; bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
@ -209,7 +218,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
texCall += "Offsets"; texCall += "Offsets";
} }
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp); int srcIndex = isBindless ? 1 : 0;
string Src(VariableType type)
{
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
}
string indexExpr = null;
if (isIndexed)
{
indexExpr = Src(VariableType.S32);
}
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
texCall += "(" + samplerName; texCall += "(" + samplerName;
@ -249,13 +272,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
hasExtraCompareArg = true; hasExtraCompareArg = true;
} }
int srcIndex = isBindless ? 1 : 0;
string Src(VariableType type)
{
return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
}
void Append(string str) void Append(string str)
{ {
texCall += ", " + str; texCall += ", " + str;
@ -397,9 +413,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp); bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
IAstNode src0 = operation.GetSource(isBindless ? 1 : 0); string indexExpr = null;
if (isIndexed)
{
indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32);
}
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
IAstNode src0 = operation.GetSource(isBindless || isIndexed ? 1 : 0);
string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0)); string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));

View file

@ -223,7 +223,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return ubName + "_" + DefaultNames.UniformNameSuffix; return ubName + "_" + DefaultNames.UniformNameSuffix;
} }
public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp) public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
{ {
string suffix; string suffix;
@ -235,16 +235,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
else else
{ {
suffix = (texOp.Handle - 8).ToString(); suffix = texOp.Handle.ToString();
if ((texOp.Type & SamplerType.Indexed) != 0)
{
suffix += $"a[{indexExpr}]";
}
} }
return GetShaderStagePrefix(stage) + "_" + DefaultNames.SamplerNamePrefix + suffix; return GetShaderStagePrefix(stage) + "_" + DefaultNames.SamplerNamePrefix + suffix;
} }
public static string GetImageName(ShaderStage stage, AstTextureOperation texOp) public static string GetImageName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
{ {
string suffix = texOp.Handle.ToString(); string suffix = texOp.Handle.ToString();
if ((texOp.Type & SamplerType.Indexed) != 0)
{
suffix += $"a[{indexExpr}]";
}
return GetShaderStagePrefix(stage) + "_" + DefaultNames.ImageNamePrefix + suffix; return GetShaderStagePrefix(stage) + "_" + DefaultNames.ImageNamePrefix + suffix;
} }

View file

@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
{ {
class TextureOperation : Operation class TextureOperation : Operation
{ {
public SamplerType Type { get; } public SamplerType Type { get; private set; }
public TextureFlags Flags { get; } public TextureFlags Flags { get; private set; }
public int Handle { get; } public int Handle { get; private set; }
public TextureOperation( public TextureOperation(
Instruction inst, Instruction inst,
@ -20,5 +20,14 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Flags = flags; Flags = flags;
Handle = handle; Handle = handle;
} }
public void TurnIntoIndexed(int handle)
{
Type |= SamplerType.Indexed;
Flags &= ~TextureFlags.Bindless;
Handle = handle;
}
} }
} }

View file

@ -14,8 +14,9 @@ namespace Ryujinx.Graphics.Shader
Mask = 0xff, Mask = 0xff,
Array = 1 << 8, Array = 1 << 8,
Multisample = 1 << 9, Indexed = 1 << 9,
Shadow = 1 << 10 Multisample = 1 << 10,
Shadow = 1 << 11
} }
static class SamplerTypeExtensions static class SamplerTypeExtensions

View file

@ -8,18 +8,21 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public TextureFlags Flags { get; } public TextureFlags Flags { get; }
public int Handle { get; } public int Handle { get; }
public int ArraySize { get; }
public AstTextureOperation( public AstTextureOperation(
Instruction inst, Instruction inst,
SamplerType type, SamplerType type,
TextureFlags flags, TextureFlags flags,
int handle, int handle,
int arraySize,
int compMask, int compMask,
params IAstNode[] sources) : base(inst, compMask, sources) params IAstNode[] sources) : base(inst, compMask, sources)
{ {
Type = type; Type = type;
Flags = flags; Flags = flags;
Handle = handle; Handle = handle;
ArraySize = arraySize;
} }
} }
} }

View file

@ -60,6 +60,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
texOp.Type, texOp.Type,
texOp.Flags, texOp.Flags,
texOp.Handle, texOp.Handle,
4, // TODO: Non-hardcoded array size.
componentMask, componentMask,
sources); sources);
} }

View file

@ -0,0 +1,84 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System.Collections.Generic;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class BindlessToIndexed
{
private const int StorageDescsBaseOffset = 0x44; // In words.
private const int UbeStorageDescsBaseOffset = 0x84; // In words.
private const int UbeStorageMaxCount = 14;
private const int StorageDescSize = 4; // In words.
private const int StorageMaxCount = 16;
private const int StorageDescsSize = StorageDescSize * StorageMaxCount;
public static void RunPass(BasicBlock block)
{
// We can turn a bindless texture access into a indexed access,
// as long the following conditions are true:
// - The handle is loaded using a LDC instruction.
// - The handle is loaded from the constant buffer with the handles (CB2 for NVN).
// - The load has a constant offset.
// The base offset of the array of handles on the constant buffer is the constant offset.
for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
{
if (!(node.Value is TextureOperation texOp))
{
continue;
}
if ((texOp.Flags & TextureFlags.Bindless) == 0)
{
continue;
}
if (!(texOp.GetSource(0).AsgOp is Operation handleAsgOp))
{
continue;
}
if (handleAsgOp.Inst != Instruction.LoadConstant)
{
continue;
}
Operand ldcSrc0 = handleAsgOp.GetSource(0);
Operand ldcSrc1 = handleAsgOp.GetSource(1);
if (ldcSrc0.Type != OperandType.Constant || ldcSrc0.Value != 2)
{
continue;
}
if (!(ldcSrc1.AsgOp is Operation addOp))
{
continue;
}
Operand addSrc1 = addOp.GetSource(1);
if (addSrc1.Type != OperandType.Constant)
{
continue;
}
texOp.TurnIntoIndexed(addSrc1.Value);
Operand index = Local();
Operand source = addOp.GetSource(0);
Operation shrBy1 = new Operation(Instruction.ShiftRightU32, index, source, Const(1));
block.Operations.AddBefore(node, shrBy1);
texOp.SetSource(0, index);
}
}
}
}

View file

@ -84,6 +84,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
} }
} }
while (modified); while (modified);
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
{
BindlessToIndexed.RunPass(blocks[blkIndex]);
}
} }
private static void PropagateCopy(Operation copyOp) private static void PropagateCopy(Operation copyOp)