Move shader resource descriptor creation out of the backend (#2290)

* Move shader resource descriptor creation out of the backend

* Remove now unused code, and other nits

* Shader cache version bump

* Nits

* Set format for bindless image load/store

* Fix buffer write flag
This commit is contained in:
gdkchan 2021-05-19 18:15:26 -03:00 committed by GitHub
parent b5c72b44de
commit 49745cfa37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 565 additions and 516 deletions

View file

@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Version of the codegen (to be changed when codegen or guest format change).
/// </summary>
private const ulong ShaderCodeGenVersion = 2261;
private const ulong ShaderCodeGenVersion = 2290;
// Progress reporting helpers
private volatile int _shaderCount;

View file

@ -1,7 +1,5 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
@ -10,22 +8,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
public const string Tab = " ";
private readonly StructuredProgramInfo _info;
public StructuredFunction CurrentFunction { get; set; }
public ShaderConfig Config { get; }
public bool CbIndexable => _info.UsesCbIndexing;
public List<BufferDescriptor> CBufferDescriptors { get; }
public List<BufferDescriptor> SBufferDescriptors { get; }
public List<TextureDescriptor> TextureDescriptors { get; }
public List<TextureDescriptor> ImageDescriptors { get; }
public OperandManager OperandManager { get; }
private StringBuilder _sb;
private readonly StructuredProgramInfo _info;
private readonly StringBuilder _sb;
private int _level;
@ -36,11 +27,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
_info = info;
Config = config;
CBufferDescriptors = new List<BufferDescriptor>();
SBufferDescriptors = new List<BufferDescriptor>();
TextureDescriptors = new List<TextureDescriptor>();
ImageDescriptors = new List<TextureDescriptor>();
OperandManager = new OperandManager();
_sb = new StringBuilder();
@ -84,23 +70,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AppendLine("}" + suffix);
}
private int FindDescriptorIndex(List<TextureDescriptor> list, AstTextureOperation texOp)
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
{
return list.FindIndex(descriptor =>
descriptor.Type == texOp.Type &&
descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format);
for (int i = 0; i < array.Length; i++)
{
var descriptor = array[i];
if (descriptor.Type == texOp.Type &&
descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format)
{
return i;
}
}
return -1;
}
public int FindTextureDescriptorIndex(AstTextureOperation texOp)
{
return FindDescriptorIndex(TextureDescriptors, texOp);
return FindDescriptorIndex(Config.GetTextureDescriptors(), texOp);
}
public int FindImageDescriptorIndex(AstTextureOperation texOp)
{
return FindDescriptorIndex(ImageDescriptors, texOp);
return FindDescriptorIndex(Config.GetImageDescriptors(), texOp);
}
public StructuredFunction GetFunction(int id)

View file

@ -70,30 +70,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine();
}
if (info.CBuffers.Count != 0)
var cBufferDescriptors = context.Config.GetConstantBufferDescriptors();
if (cBufferDescriptors.Length != 0)
{
DeclareUniforms(context, info);
DeclareUniforms(context, cBufferDescriptors);
context.AppendLine();
}
if (info.SBuffers.Count != 0)
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
if (sBufferDescriptors.Length != 0)
{
DeclareStorages(context, info);
DeclareStorages(context, sBufferDescriptors);
context.AppendLine();
}
if (info.Samplers.Count != 0)
var textureDescriptors = context.Config.GetTextureDescriptors();
if (textureDescriptors.Length != 0)
{
DeclareSamplers(context, info);
DeclareSamplers(context, textureDescriptors);
context.AppendLine();
}
if (info.Images.Count != 0)
var imageDescriptors = context.Config.GetImageDescriptors();
if (imageDescriptors.Length != 0)
{
DeclareImages(context, info);
DeclareImages(context, imageDescriptors);
context.AppendLine();
}
@ -246,58 +250,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
throw new ArgumentException($"Invalid variable type \"{type}\".");
}
private static void DeclareUniforms(CodeGenContext context, StructuredProgramInfo info)
private static void DeclareUniforms(CodeGenContext context, BufferDescriptor[] descriptors)
{
string ubSize = "[" + NumberFormatter.FormatInt(Constants.ConstantBufferSize / 16) + "]";
if (info.UsesCbIndexing)
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
{
int count = info.CBuffers.Max() + 1;
int[] bindings = new int[count];
for (int i = 0; i < count; i++)
{
bindings[i] = context.Config.Counts.IncrementUniformBuffersCount();
}
foreach (int cbufSlot in info.CBuffers.OrderBy(x => x))
{
context.CBufferDescriptors.Add(new BufferDescriptor(bindings[cbufSlot], cbufSlot));
}
string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
ubName += "_" + DefaultNames.UniformNamePrefix;
string blockName = $"{ubName}_{DefaultNames.BlockSuffix}";
context.AppendLine($"layout (binding = {bindings[0]}, std140) uniform {blockName}");
context.AppendLine($"layout (binding = {descriptors[0].Binding}, std140) uniform {blockName}");
context.EnterScope();
context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";");
context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(count)}];");
context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(descriptors.Length)}];");
}
else
{
foreach (int cbufSlot in info.CBuffers.OrderBy(x => x))
foreach (var descriptor in descriptors)
{
int binding = context.Config.Counts.IncrementUniformBuffersCount();
context.CBufferDescriptors.Add(new BufferDescriptor(binding, cbufSlot));
string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot;
ubName += "_" + DefaultNames.UniformNamePrefix + descriptor.Slot;
context.AppendLine($"layout (binding = {binding}, std140) uniform {ubName}");
context.AppendLine($"layout (binding = {descriptor.Binding}, std140) uniform {ubName}");
context.EnterScope();
context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, cbufSlot, false) + ubSize + ";");
context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, descriptor.Slot, false) + ubSize + ";");
context.LeaveScope(";");
}
}
}
private static void DeclareStorages(CodeGenContext context, StructuredProgramInfo info)
private static void DeclareStorages(CodeGenContext context, BufferDescriptor[] descriptors)
{
string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
@ -305,130 +291,81 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
string blockName = $"{sbName}_{DefaultNames.BlockSuffix}";
int count = info.SBuffers.Max() + 1;
int[] bindings = new int[count];
for (int i = 0; i < count; i++)
{
bindings[i] = context.Config.Counts.IncrementStorageBuffersCount();
}
foreach (int sbufSlot in info.SBuffers)
{
context.SBufferDescriptors.Add(new BufferDescriptor(bindings[sbufSlot], sbufSlot));
}
context.AppendLine($"layout (binding = {bindings[0]}, std430) buffer {blockName}");
context.AppendLine($"layout (binding = {descriptors[0].Binding}, std430) buffer {blockName}");
context.EnterScope();
context.AppendLine("uint " + DefaultNames.DataName + "[];");
context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(count)}];");
context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(descriptors.Length)}];");
}
private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo info)
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
{
HashSet<string> samplers = new HashSet<string>();
// Texture instructions other than TextureSample (like TextureSize)
// may have incomplete sampler type information. In those cases,
// we prefer instead the more accurate information from the
// TextureSample instruction, if both are available.
foreach (AstTextureOperation texOp in info.Samplers.OrderBy(x => x.Handle * 2 + (x.Inst == Instruction.TextureSample ? 0 : 1)))
int arraySize = 0;
foreach (var descriptor in descriptors)
{
string indexExpr = NumberFormatter.FormatInt(texOp.ArraySize);
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
if ((texOp.Flags & TextureFlags.Bindless) != 0 || !samplers.Add(samplerName))
if (descriptor.Type.HasFlag(SamplerType.Indexed))
{
continue;
}
int firstBinding = -1;
if ((texOp.Type & SamplerType.Indexed) != 0)
{
for (int index = 0; index < texOp.ArraySize; index++)
if (arraySize == 0)
{
int binding = context.Config.Counts.IncrementTexturesCount();
if (firstBinding < 0)
{
firstBinding = binding;
}
var desc = new TextureDescriptor(binding, texOp.Type, texOp.Format, texOp.CbufSlot, texOp.Handle + index * 2);
context.TextureDescriptors.Add(desc);
arraySize = ShaderConfig.SamplerArraySize;
}
else if (--arraySize != 0)
{
continue;
}
}
else
{
firstBinding = context.Config.Counts.IncrementTexturesCount();
var desc = new TextureDescriptor(firstBinding, texOp.Type, texOp.Format, texOp.CbufSlot, texOp.Handle);
string indexExpr = NumberFormatter.FormatInt(arraySize);
context.TextureDescriptors.Add(desc);
}
string samplerName = OperandManager.GetSamplerName(
context.Config.Stage,
descriptor.CbufSlot,
descriptor.HandleIndex,
descriptor.Type.HasFlag(SamplerType.Indexed),
indexExpr);
string samplerTypeName = texOp.Type.ToGlslSamplerType();
string samplerTypeName = descriptor.Type.ToGlslSamplerType();
context.AppendLine($"layout (binding = {firstBinding}) uniform {samplerTypeName} {samplerName};");
context.AppendLine($"layout (binding = {descriptor.Binding}) uniform {samplerTypeName} {samplerName};");
}
}
private static void DeclareImages(CodeGenContext context, StructuredProgramInfo info)
private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors)
{
HashSet<string> images = new HashSet<string>();
foreach (AstTextureOperation texOp in info.Images.OrderBy(x => x.Handle))
int arraySize = 0;
foreach (var descriptor in descriptors)
{
string indexExpr = NumberFormatter.FormatInt(texOp.ArraySize);
string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr);
if ((texOp.Flags & TextureFlags.Bindless) != 0 || !images.Add(imageName))
if (descriptor.Type.HasFlag(SamplerType.Indexed))
{
continue;
}
int firstBinding = -1;
if ((texOp.Type & SamplerType.Indexed) != 0)
{
for (int index = 0; index < texOp.ArraySize; index++)
if (arraySize == 0)
{
int binding = context.Config.Counts.IncrementImagesCount();
if (firstBinding < 0)
{
firstBinding = binding;
}
var desc = new TextureDescriptor(binding, texOp.Type, texOp.Format, texOp.CbufSlot, texOp.Handle + index * 2);
context.ImageDescriptors.Add(desc);
arraySize = ShaderConfig.SamplerArraySize;
}
else if (--arraySize != 0)
{
continue;
}
}
else
{
firstBinding = context.Config.Counts.IncrementImagesCount();
var desc = new TextureDescriptor(firstBinding, texOp.Type, texOp.Format, texOp.CbufSlot, texOp.Handle);
string indexExpr = NumberFormatter.FormatInt(arraySize);
context.ImageDescriptors.Add(desc);
}
string imageName = OperandManager.GetImageName(
context.Config.Stage,
descriptor.CbufSlot,
descriptor.HandleIndex,
descriptor.Format,
descriptor.Type.HasFlag(SamplerType.Indexed),
indexExpr);
string layout = texOp.Format.ToGlslFormat();
string layout = descriptor.Format.ToGlslFormat();
if (!string.IsNullOrEmpty(layout))
{
layout = ", " + layout;
}
string imageTypeName = texOp.Type.ToGlslImageType(texOp.Format.GetComponentType());
string imageTypeName = descriptor.Type.ToGlslImageType(descriptor.Format.GetComponentType());
context.AppendLine($"layout (binding = {firstBinding}{layout}) uniform {imageTypeName} {imageName};");
context.AppendLine($"layout (binding = {descriptor.Binding}{layout}) uniform {imageTypeName} {imageName};");
}
}
@ -528,7 +465,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage);
int scaleElements = context.TextureDescriptors.Count + context.ImageDescriptors.Count;
int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length;
if (context.Config.Stage == ShaderStage.Fragment)
{

View file

@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
private const string MainFunctionName = "main";
public static GlslProgram Generate(StructuredProgramInfo info, ShaderConfig config)
public static string Generate(StructuredProgramInfo info, ShaderConfig config)
{
CodeGenContext context = new CodeGenContext(info, config);
@ -37,12 +37,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
PrintFunction(context, info, info.Functions[0], MainFunctionName);
return new GlslProgram(
context.CBufferDescriptors.ToArray(),
context.SBufferDescriptors.ToArray(),
context.TextureDescriptors.ToArray(),
context.ImageDescriptors.ToArray(),
context.GetCode());
return context.GetCode();
}
private static void PrintFunction(CodeGenContext context, StructuredProgramInfo info, StructuredFunction function, string funcName = null)

View file

@ -1,26 +0,0 @@
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
class GlslProgram
{
public BufferDescriptor[] CBufferDescriptors { get; }
public BufferDescriptor[] SBufferDescriptors { get; }
public TextureDescriptor[] TextureDescriptors { get; }
public TextureDescriptor[] ImageDescriptors { get; }
public string Code { get; }
public GlslProgram(
BufferDescriptor[] cBufferDescriptors,
BufferDescriptor[] sBufferDescriptors,
TextureDescriptor[] textureDescriptors,
TextureDescriptor[] imageDescriptors,
string code)
{
CBufferDescriptors = cBufferDescriptors;
SBufferDescriptors = sBufferDescriptors;
TextureDescriptors = textureDescriptors;
ImageDescriptors = imageDescriptors;
Code = code;
}
}
}

View file

@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
}
else if (node is AstOperand operand)
{
return context.OperandManager.GetExpression(operand, context.Config, context.CbIndexable);
return context.OperandManager.GetExpression(operand, context.Config);
}
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");
@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
switch (memRegion)
{
case Instruction.MrShared: args += LoadShared(context, operation); break;
case Instruction.MrStorage: args += LoadStorage(context, operation, forAtomic: true); break;
case Instruction.MrStorage: args += LoadStorage(context, operation); break;
default: throw new InvalidOperationException($"Invalid memory region \"{memRegion}\".");
}

View file

@ -56,7 +56,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string ApplyScaling(string vector)
{
int index = context.FindImageDescriptorIndex(texOp);
TextureUsageFlags flags = TextureUsageFlags.NeedsScaleValue;
if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) &&
texOp.Inst == Instruction.ImageLoad &&
@ -64,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
!isIndexed)
{
// Image scales start after texture ones.
int scaleIndex = context.TextureDescriptors.Count + index;
int scaleIndex = context.Config.GetTextureDescriptors().Length + index;
if (pCount == 3 && isArray)
{
@ -75,19 +74,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
vector = "Helper_TexelFetchScale(" + vector + ", " + scaleIndex + ")";
}
else
{
flags |= TextureUsageFlags.ResScaleUnsupported;
}
}
else
{
flags |= TextureUsageFlags.ResScaleUnsupported;
}
if (!isBindless)
{
context.ImageDescriptors[index] = context.ImageDescriptors[index].SetFlag(flags);
}
return vector;
@ -112,7 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (texOp.Inst == Instruction.ImageStore)
{
int texIndex = context.FindImageDescriptorIndex(texOp);
context.ImageDescriptors[texIndex] = context.ImageDescriptors[texIndex].SetFlag(TextureUsageFlags.ImageStore);
VariableType type = texOp.Format.GetComponentType();
@ -176,12 +161,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (src1 is AstOperand oper && oper.Type == OperandType.Constant)
{
return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, context.Config.Stage, context.CbIndexable);
bool cbIndexable = context.Config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing);
return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, context.Config.Stage, cbIndexable);
}
else
{
string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, context.Config.Stage);
}
}
@ -205,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return $"{arrayName}[{offsetExpr}]";
}
public static string LoadStorage(CodeGenContext context, AstOperation operation, bool forAtomic = false)
public static string LoadStorage(CodeGenContext context, AstOperation operation)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
@ -213,11 +198,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
if (forAtomic)
{
SetStorageWriteFlag(context, src1, context.Config.Stage);
}
return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
}
@ -306,7 +286,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32);
SetStorageWriteFlag(context, src1, context.Config.Stage);
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
return $"{sb} = {src}";
@ -471,7 +450,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (intCoords)
{
int index = context.FindTextureDescriptorIndex(texOp);
TextureUsageFlags flags = TextureUsageFlags.NeedsScaleValue;
if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) &&
!isBindless &&
@ -486,22 +464,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
vector = "Helper_TexelFetchScale(" + vector + ", " + index + ")";
}
else
{
flags |= TextureUsageFlags.ResScaleUnsupported;
}
}
else
{
// Resolution scaling cannot be applied to this texture right now.
// Flag so that we know to blacklist scaling on related textures when binding them.
flags |= TextureUsageFlags.ResScaleUnsupported;
}
if (!isBindless)
{
context.TextureDescriptors[index] = context.TextureDescriptors[index].SetFlag(flags);
}
}
@ -638,32 +600,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
}
}
private static void SetStorageWriteFlag(CodeGenContext context, IAstNode indexExpr, ShaderStage stage)
{
// Attempt to find a BufferDescriptor with the given index.
// If it cannot be resolved or is not constant, assume that the slot expression could potentially index any of them,
// and set the flag on all storage buffers.
int index = -1;
if (indexExpr is AstOperand operand && operand.Type == OperandType.Constant)
{
index = context.SBufferDescriptors.FindIndex(buffer => buffer.Slot == operand.Value);
}
if (index != -1)
{
context.SBufferDescriptors[index] = context.SBufferDescriptors[index].SetFlag(BufferUsageFlags.Write);
}
else
{
for (int i = 0; i < context.SBufferDescriptors.Count; i++)
{
context.SBufferDescriptors[i] = context.SBufferDescriptors[i].SetFlag(BufferUsageFlags.Write);
}
}
}
private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage)
{
string sbName = OperandManager.GetShaderStagePrefix(stage);

View file

@ -94,30 +94,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return name;
}
public string GetExpression(AstOperand operand, ShaderConfig config, bool cbIndexable)
public string GetExpression(AstOperand operand, ShaderConfig config)
{
switch (operand.Type)
return operand.Type switch
{
case OperandType.Argument:
return GetArgumentName(operand.Value);
case OperandType.Attribute:
return GetAttributeName(operand, config);
case OperandType.Constant:
return NumberFormatter.FormatInt(operand.Value);
case OperandType.ConstantBuffer:
return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, cbIndexable);
case OperandType.LocalVariable:
return _locals[operand];
case OperandType.Undefined:
return DefaultNames.UndefinedName;
}
throw new ArgumentException($"Invalid operand type \"{operand.Type}\".");
OperandType.Argument => GetArgumentName(operand.Value),
OperandType.Attribute => GetAttributeName(operand, config),
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
OperandType.ConstantBuffer => GetConstantBufferName(
operand.CbufSlot,
operand.CbufOffset,
config.Stage,
config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)),
OperandType.LocalVariable => _locals[operand],
OperandType.Undefined => DefaultNames.UndefinedName,
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
};
}
public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable)
@ -242,9 +234,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
{
string suffix = texOp.CbufSlot < 0 ? $"_tcb_{texOp.Handle:X}" : $"_cb{texOp.CbufSlot}_{texOp.Handle:X}";
return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr);
}
if ((texOp.Type & SamplerType.Indexed) != 0)
public static string GetSamplerName(ShaderStage stage, int cbufSlot, int handle, bool indexed, string indexExpr)
{
string suffix = cbufSlot < 0 ? $"_tcb_{handle:X}" : $"_cb{cbufSlot}_{handle:X}";
if (indexed)
{
suffix += $"a[{indexExpr}]";
}
@ -254,9 +251,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public static string GetImageName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
{
string suffix = texOp.CbufSlot < 0 ? $"_tcb_{texOp.Handle:X}_{texOp.Format.ToGlslFormat()}" : $"_cb{texOp.CbufSlot}_{texOp.Handle:X}_{texOp.Format.ToGlslFormat()}";
return GetImageName(stage, texOp.CbufSlot, texOp.Handle, texOp.Format, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr);
}
if ((texOp.Type & SamplerType.Indexed) != 0)
public static string GetImageName(
ShaderStage stage,
int cbufSlot,
int handle,
TextureFormat format,
bool indexed,
string indexExpr)
{
string suffix = cbufSlot < 0
? $"_tcb_{handle:X}_{format.ToGlslFormat()}"
: $"_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
if (indexed)
{
suffix += $"a[{indexExpr}]";
}

View file

@ -82,7 +82,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
switch (context.CurrOp)
{
case IOpCodeCbuf op:
return context.PackDouble2x32(Cbuf(op.Slot, op.Offset), Cbuf(op.Slot, op.Offset + 1));
return context.PackDouble2x32(
context.Config.CreateCbuf(op.Slot, op.Offset),
context.Config.CreateCbuf(op.Slot, op.Offset + 1));
case IOpCodeImmF op:
return context.FP32ConvertToFP64(ConstF(op.Immediate));
@ -99,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
switch (context.CurrOp)
{
case IOpCodeCbuf op:
return Cbuf(op.Slot, op.Offset);
return context.Config.CreateCbuf(op.Slot, op.Offset);
case IOpCodeImm op:
return Const(op.Immediate);
@ -125,7 +127,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
switch (context.CurrOp)
{
case IOpCodeRegCbuf op:
return context.PackDouble2x32(Cbuf(op.Slot, op.Offset), Cbuf(op.Slot, op.Offset + 1));
return context.PackDouble2x32(
context.Config.CreateCbuf(op.Slot, op.Offset),
context.Config.CreateCbuf(op.Slot, op.Offset + 1));
case IOpCodeRc op:
return context.PackDouble2x32(Register(op.Rc.Index, op.Rc.Type), Register(op.Rc.Index | 1, op.Rc.Type));
@ -136,7 +140,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
switch (context.CurrOp)
{
case IOpCodeRegCbuf op:
return Cbuf(op.Slot, op.Offset);
return context.Config.CreateCbuf(op.Slot, op.Offset);
case IOpCodeRc op:
return Register(op.Rc);

View file

@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand rd = Register(rdIndex++, RegisterType.Gpr);
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
Instruction.ImageLoad,
type,
flags,
@ -132,17 +132,15 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand rd = Register(rdIndex++, RegisterType.Gpr);
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
Instruction.ImageLoad,
type,
GetTextureFormat(op.Size),
flags,
handle,
compIndex,
rd,
sources)
{
Format = GetTextureFormat(op.Size)
};
sources);
context.Add(operation);
@ -266,17 +264,15 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureFlags flags = op.IsBindless ? TextureFlags.Bindless : TextureFlags.None;
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
Instruction.ImageStore,
type,
format,
flags,
handle,
0,
null,
sources)
{
Format = format
};
sources);
context.Add(operation);
}
@ -615,7 +611,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
Operand dest = GetDest();
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
@ -764,7 +760,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
Operand dest = GetDest();
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
@ -888,7 +884,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
Operand tempDest = Local();
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
Instruction.Lod,
type,
flags,
@ -1027,7 +1023,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
Operand dest = GetDest();
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
@ -1112,7 +1108,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
Operand dest = GetDest();
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
inst,
type,
flags,
@ -1277,7 +1273,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
Operand dest = GetDest();
TextureOperation operation = new TextureOperation(
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,

View file

@ -2,30 +2,30 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
{
class TextureOperation : Operation
{
private const int DefaultCbufSlot = -1;
public const int DefaultCbufSlot = -1;
public SamplerType Type { get; private set; }
public SamplerType Type { get; private set; }
public TextureFormat Format { get; set; }
public TextureFlags Flags { get; private set; }
public int CbufSlot { get; private set; }
public int Handle { get; private set; }
public TextureFormat Format { get; set; }
public TextureOperation(
Instruction inst,
SamplerType type,
TextureFlags flags,
int handle,
int compIndex,
Operand dest,
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int handle,
int compIndex,
Operand dest,
params Operand[] sources) : base(inst, compIndex, dest, sources)
{
Type = type;
Flags = flags;
Type = type;
Format = format;
Flags = flags;
CbufSlot = DefaultCbufSlot;
Handle = handle;
Handle = handle;
}
public void TurnIntoIndexed(int handle)
@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
}
CbufSlot = cbufSlot;
Handle = handle;
Handle = handle;
}
}
}

View file

@ -4,31 +4,28 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
class AstTextureOperation : AstOperation
{
public SamplerType Type { get; }
public SamplerType Type { get; }
public TextureFormat Format { get; }
public TextureFlags Flags { get; }
public TextureFlags Flags { get; }
public int CbufSlot { get; }
public int Handle { get; }
public int ArraySize { get; }
public int CbufSlot { get; }
public int Handle { get; }
public AstTextureOperation(
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int cbufSlot,
int handle,
int arraySize,
int index,
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int cbufSlot,
int handle,
int index,
params IAstNode[] sources) : base(inst, index, sources, sources.Length)
{
Type = type;
Format = format;
Flags = flags;
CbufSlot = cbufSlot;
Handle = handle;
ArraySize = arraySize;
Type = type;
Format = format;
Flags = flags;
CbufSlot = cbufSlot;
Handle = handle;
}
}
}

View file

@ -2,7 +2,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Numerics;
namespace Ryujinx.Graphics.Shader.StructuredIr
{
@ -100,7 +99,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
texOp.Flags,
texOp.CbufSlot,
texOp.Handle,
4, // TODO: Non-hardcoded array size.
texOp.Index,
sources);
}
@ -109,34 +107,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
AstOperand dest = context.GetOperandDef(operation.Dest);
if (inst == Instruction.LoadConstant)
{
Operand slot = operation.GetSource(0);
if (slot.Type == OperandType.Constant)
{
context.Info.CBuffers.Add(slot.Value);
}
else
{
// If the value is not constant, then we don't know
// how many constant buffers are used, so we assume
// all of them are used.
int cbCount = 32 - BitOperations.LeadingZeroCount(context.Config.GpuAccessor.QueryConstantBufferUse());
for (int index = 0; index < cbCount; index++)
{
context.Info.CBuffers.Add(index);
}
context.Info.UsesCbIndexing = true;
}
}
else if (UsesStorage(inst))
{
AddSBufferUse(context.Info.SBuffers, operation);
}
// If all the sources are bool, it's better to use short-circuiting
// logical operations, rather than forcing a cast to int and doing
// a bitwise operation with the value, as it is likely to be used as
@ -169,23 +139,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
if (operation is TextureOperation texOp)
{
if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore)
if (texOp.Inst == Instruction.ImageLoad)
{
dest.VarType = texOp.Format.GetComponentType();
}
AstTextureOperation astTexOp = GetAstTextureOperation(texOp);
if (texOp.Inst == Instruction.ImageLoad)
{
context.Info.Images.Add(astTexOp);
}
else
{
context.Info.Samplers.Add(astTexOp);
}
source = astTexOp;
source = GetAstTextureOperation(texOp);
}
else if (!isCopy)
{
@ -206,17 +165,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
AstTextureOperation astTexOp = GetAstTextureOperation(texOp);
context.Info.Images.Add(astTexOp);
context.AddNode(astTexOp);
}
else
{
if (UsesStorage(inst))
{
AddSBufferUse(context.Info.SBuffers, operation);
}
context.AddNode(new AstOperation(inst, operation.Index, sources, operation.SourcesCount));
}
@ -257,26 +209,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
}
}
private static void AddSBufferUse(HashSet<int> sBuffers, Operation operation)
{
Operand slot = operation.GetSource(0);
if (slot.Type == OperandType.Constant)
{
sBuffers.Add(slot.Value);
}
else
{
// If the value is not constant, then we don't know
// how many storage buffers are used, so we assume
// all of them are used.
for (int index = 0; index < GlobalMemory.StorageMaxCount; index++)
{
sBuffers.Add(index);
}
}
}
private static VariableType GetVarTypeFromUses(Operand dest)
{
HashSet<Operand> visited = new HashSet<Operand>();
@ -301,7 +233,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
foreach (INode useNode in operand.UseOps)
{
if (!(useNode is Operation operation))
if (useNode is not Operation operation)
{
continue;
}
@ -340,7 +272,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
foreach (IAstNode node in sources)
{
if (!(node is AstOperand operand))
if (node is not AstOperand operand)
{
return false;
}
@ -356,52 +288,37 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
private static bool IsBranchInst(Instruction inst)
{
switch (inst)
return inst switch
{
case Instruction.Branch:
case Instruction.BranchIfFalse:
case Instruction.BranchIfTrue:
return true;
}
return false;
Instruction.Branch or
Instruction.BranchIfFalse or
Instruction.BranchIfTrue => true,
_ => false,
};
}
private static bool IsBitwiseInst(Instruction inst)
{
switch (inst)
return inst switch
{
case Instruction.BitwiseAnd:
case Instruction.BitwiseExclusiveOr:
case Instruction.BitwiseNot:
case Instruction.BitwiseOr:
return true;
}
return false;
Instruction.BitwiseAnd or
Instruction.BitwiseExclusiveOr or
Instruction.BitwiseNot or
Instruction.BitwiseOr => true,
_ => false
};
}
private static Instruction GetLogicalFromBitwiseInst(Instruction inst)
{
switch (inst)
return inst switch
{
case Instruction.BitwiseAnd: return Instruction.LogicalAnd;
case Instruction.BitwiseExclusiveOr: return Instruction.LogicalExclusiveOr;
case Instruction.BitwiseNot: return Instruction.LogicalNot;
case Instruction.BitwiseOr: return Instruction.LogicalOr;
}
throw new ArgumentException($"Unexpected instruction \"{inst}\".");
}
private static bool UsesStorage(Instruction inst)
{
if (inst == Instruction.LoadStorage || inst == Instruction.StoreStorage)
{
return true;
}
return inst.IsAtomic() && (inst & Instruction.MrMask) == Instruction.MrStorage;
Instruction.BitwiseAnd => Instruction.LogicalAnd,
Instruction.BitwiseExclusiveOr => Instruction.LogicalExclusiveOr,
Instruction.BitwiseNot => Instruction.LogicalNot,
Instruction.BitwiseOr => Instruction.LogicalOr,
_ => throw new ArgumentException($"Unexpected instruction \"{inst}\".")
};
}
}
}

View file

@ -291,10 +291,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
Info.IAttributes.Add(attrIndex);
}
else if (operand.Type == OperandType.ConstantBuffer)
{
Info.CBuffers.Add(operand.GetCbufSlot());
}
return GetOperand(operand);
}

View file

@ -6,31 +6,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
public List<StructuredFunction> Functions { get; }
public HashSet<int> CBuffers { get; }
public HashSet<int> SBuffers { get; }
public HashSet<int> IAttributes { get; }
public HashSet<int> OAttributes { get; }
public bool UsesCbIndexing { get; set; }
public HelperFunctionsMask HelperFunctionsMask { get; set; }
public HashSet<AstTextureOperation> Samplers { get; }
public HashSet<AstTextureOperation> Images { get; }
public StructuredProgramInfo()
{
Functions = new List<StructuredFunction>();
CBuffers = new HashSet<int>();
SBuffers = new HashSet<int>();
IAttributes = new HashSet<int>();
OAttributes = new HashSet<int>();
Samplers = new HashSet<AstTextureOperation>();
Images = new HashSet<AstTextureOperation>();
}
}
}

View file

@ -53,6 +53,36 @@ namespace Ryujinx.Graphics.Shader.Translation
_operations.Add(operation);
}
public TextureOperation CreateTextureOperation(
Instruction inst,
SamplerType type,
TextureFlags flags,
int handle,
int compIndex,
Operand dest,
params Operand[] sources)
{
return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dest, sources);
}
public TextureOperation CreateTextureOperation(
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int handle,
int compIndex,
Operand dest,
params Operand[] sources)
{
if (!flags.HasFlag(TextureFlags.Bindless))
{
Config.SetUsedTexture(inst, type, format, flags, TextureOperation.DefaultCbufSlot, handle);
}
return new TextureOperation(inst, type, format, flags, handle, compIndex, dest, sources);
}
public void FlagAttributeRead(int attribute)
{
if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId)

View file

@ -518,6 +518,15 @@ namespace Ryujinx.Graphics.Shader.Translation
public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b)
{
if (a.Type == OperandType.Constant)
{
context.Config.SetUsedConstantBuffer(a.Value);
}
else
{
context.Config.SetUsedFeature(FeatureFlags.CbIndexing);
}
return context.Add(Instruction.LoadConstant, Local(), a, b);
}

View file

@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation
FragCoordXY = 1 << 1,
Bindless = 1 << 2,
InstanceId = 1 << 3
InstanceId = 1 << 3,
CbIndexing = 1 << 4
}
}

View file

@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (bindlessHandle.Type == OperandType.ConstantBuffer)
{
texOp.SetHandle(bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot());
SetHandle(config, texOp, bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot());
continue;
}
@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue;
}
texOp.SetHandle(src0.GetCbufOffset() | (src1.GetCbufOffset() << 16), src0.GetCbufSlot());
SetHandle(config, texOp, src0.GetCbufOffset() | (src1.GetCbufOffset() << 16), src0.GetCbufSlot());
}
else if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore)
{
@ -64,11 +64,19 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (src0.Type == OperandType.ConstantBuffer)
{
texOp.SetHandle(src0.GetCbufOffset(), src0.GetCbufSlot());
texOp.Format = config.GetTextureFormat(texOp.Handle, texOp.CbufSlot);
int cbufOffset = src0.GetCbufOffset();
int cbufSlot = src0.GetCbufSlot();
texOp.Format = config.GetTextureFormat(cbufOffset, cbufSlot);
SetHandle(config, texOp, cbufOffset, cbufSlot);
}
}
}
}
private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot)
{
texOp.SetHandle(cbufOffset, cbufSlot);
config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, cbufSlot, cbufOffset);
}
}
}

View file

@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class BindlessToIndexed
{
public static void RunPass(BasicBlock block)
public static void RunPass(BasicBlock block, ShaderConfig config)
{
// We can turn a bindless texture access into a indexed access,
// as long the following conditions are true:
@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue;
}
texOp.TurnIntoIndexed(addSrc1.Value / 4);
TurnIntoIndexed(config, texOp, addSrc1.Value / 4);
Operand index = Local();
@ -75,5 +75,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
texOp.SetSource(0, index);
}
}
private static void TurnIntoIndexed(ShaderConfig config, TextureOperation texOp, int handle)
{
texOp.TurnIntoIndexed(handle);
config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, handle);
}
}
}

View file

@ -58,11 +58,16 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
Operation operation = (Operation)node.Value;
bool isAtomic = operation.Inst.IsAtomic();
bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal;
config.SetUsedStorageBuffer(storageIndex, isWrite);
Operand GetStorageOffset()
{
Operand addrLow = operation.GetSource(0);
Operand baseAddrLow = Cbuf(0, GetStorageCbOffset(config.Stage, storageIndex));
Operand baseAddrLow = config.CreateCbuf(0, GetStorageCbOffset(config.Stage, storageIndex));
Operand baseAddrTrunc = Local();
@ -96,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
Operation storageOp;
if (operation.Inst.IsAtomic())
if (isAtomic)
{
Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage;
@ -133,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
Operand addrLow = operation.GetSource(0);
Operand baseAddrLow = Cbuf(0, UbeBaseOffset + storageIndex * StorageDescSize);
Operand baseAddrLow = config.CreateCbuf(0, UbeBaseOffset + storageIndex * StorageDescSize);
Operand baseAddrTrunc = Local();
@ -157,9 +162,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
Operand[] sources = new Operand[operation.SourcesCount];
sources[0] = Const(UbeFirstCbuf + storageIndex);
int cbSlot = UbeFirstCbuf + storageIndex;
sources[0] = Const(cbSlot);
sources[1] = GetCbufOffset();
config.SetUsedConstantBuffer(cbSlot);
for (int index = 2; index < operation.SourcesCount; index++)
{
sources[index] = operation.GetSource(index);

View file

@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
{
GlobalToStorage.RunPass(blocks[blkIndex], config);
BindlessToIndexed.RunPass(blocks[blkIndex]);
BindlessToIndexed.RunPass(blocks[blkIndex], config);
BindlessElimination.RunPass(blocks[blkIndex], config);
}

View file

@ -48,6 +48,9 @@ namespace Ryujinx.Graphics.Shader.Translation
{
Operation operation = (Operation)node.Value;
bool isAtomic = operation.Inst.IsAtomic();
bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal;
Operation storageOp;
Operand PrependOperation(Instruction inst, params Operand[] sources)
@ -67,11 +70,13 @@ namespace Ryujinx.Graphics.Shader.Translation
for (int slot = 0; slot < StorageMaxCount; slot++)
{
config.SetUsedStorageBuffer(slot, isWrite);
int cbOffset = GetStorageCbOffset(config.Stage, slot);
Operand baseAddrLow = Cbuf(0, cbOffset);
Operand baseAddrHigh = Cbuf(0, cbOffset + 1);
Operand size = Cbuf(0, cbOffset + 2);
Operand baseAddrLow = config.CreateCbuf(0, cbOffset);
Operand baseAddrHigh = config.CreateCbuf(0, cbOffset + 1);
Operand size = config.CreateCbuf(0, cbOffset + 2);
Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow);
Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow);
@ -104,7 +109,7 @@ namespace Ryujinx.Graphics.Shader.Translation
sources[index] = operation.GetSource(index);
}
if (operation.Inst.IsAtomic())
if (isAtomic)
{
Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage;
@ -303,6 +308,7 @@ namespace Ryujinx.Graphics.Shader.Translation
node.List.AddBefore(node, new TextureOperation(
Instruction.TextureSize,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.Handle,
index,
@ -350,6 +356,7 @@ namespace Ryujinx.Graphics.Shader.Translation
node.List.AddBefore(node, new TextureOperation(
Instruction.Lod,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.Handle,
1,
@ -374,6 +381,7 @@ namespace Ryujinx.Graphics.Shader.Translation
node.List.AddBefore(node, new TextureOperation(
Instruction.TextureSize,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.Handle,
index,
@ -409,6 +417,7 @@ namespace Ryujinx.Graphics.Shader.Translation
TextureOperation newTexOp = new TextureOperation(
Instruction.TextureSample,
texOp.Type,
texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.Handle,
componentIndex,

View file

@ -1,9 +1,16 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Ryujinx.Graphics.Shader.Translation
{
class ShaderConfig
{
// TODO: Non-hardcoded array size.
public const int SamplerArraySize = 4;
public ShaderStage Stage { get; }
public bool GpPassthrough { get; }
@ -24,8 +31,6 @@ namespace Ryujinx.Graphics.Shader.Translation
public TranslationFlags Flags { get; }
public TranslationCounts Counts { get; }
public int Size { get; private set; }
public byte ClipDistancesWritten { get; private set; }
@ -34,42 +39,80 @@ namespace Ryujinx.Graphics.Shader.Translation
public HashSet<int> TextureHandlesForCache { get; }
private readonly TranslationCounts _counts;
private int _usedConstantBuffers;
private int _usedStorageBuffers;
private int _usedStorageBuffersWrite;
private struct TextureInfo : IEquatable<TextureInfo>
{
public int CbufSlot { get; }
public int Handle { get; }
public bool Indexed { get; }
public TextureFormat Format { get; }
public TextureInfo(int cbufSlot, int handle, bool indexed, TextureFormat format)
{
CbufSlot = cbufSlot;
Handle = handle;
Indexed = indexed;
Format = format;
}
public override bool Equals(object obj)
{
return obj is TextureInfo other && Equals(other);
}
public bool Equals(TextureInfo other)
{
return CbufSlot == other.CbufSlot && Handle == other.Handle && Indexed == other.Indexed && Format == other.Format;
}
public override int GetHashCode()
{
return HashCode.Combine(CbufSlot, Handle, Indexed, Format);
}
}
private struct TextureMeta
{
public bool AccurateType;
public SamplerType Type;
public TextureUsageFlags UsageFlags;
}
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
private BufferDescriptor[] _cachedConstantBufferDescriptors;
private BufferDescriptor[] _cachedStorageBufferDescriptors;
private TextureDescriptor[] _cachedTextureDescriptors;
private TextureDescriptor[] _cachedImageDescriptors;
public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
{
Stage = ShaderStage.Compute;
GpPassthrough = false;
OutputTopology = OutputTopology.PointList;
MaxOutputVertices = 0;
LocalMemorySize = 0;
ImapTypes = null;
OmapTargets = null;
OmapSampleMask = false;
OmapDepth = false;
GpuAccessor = gpuAccessor;
Flags = flags;
Size = 0;
UsedFeatures = FeatureFlags.None;
Counts = counts;
_counts = counts;
TextureHandlesForCache = new HashSet<int>();
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
}
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) : this(gpuAccessor, flags, counts)
{
Stage = header.Stage;
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
OutputTopology = header.OutputTopology;
MaxOutputVertices = header.MaxOutputVertexCount;
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
ImapTypes = header.ImapTypes;
OmapTargets = header.OmapTargets;
OmapSampleMask = header.OmapSampleMask;
OmapDepth = header.OmapDepth;
GpuAccessor = gpuAccessor;
Flags = flags;
Size = 0;
UsedFeatures = FeatureFlags.None;
Counts = counts;
TextureHandlesForCache = new HashSet<int>();
Stage = header.Stage;
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
OutputTopology = header.OutputTopology;
MaxOutputVertices = header.MaxOutputVertexCount;
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
ImapTypes = header.ImapTypes;
OmapTargets = header.OmapTargets;
OmapSampleMask = header.OmapSampleMask;
OmapDepth = header.OmapDepth;
}
public int GetDepthRegister()
@ -126,5 +169,199 @@ namespace Ryujinx.Graphics.Shader.Translation
{
UsedFeatures |= flags;
}
public Operand CreateCbuf(int slot, int offset)
{
SetUsedConstantBuffer(slot);
return OperandHelper.Cbuf(slot, offset);
}
public void SetUsedConstantBuffer(int slot)
{
_usedConstantBuffers |= 1 << slot;
}
public void SetUsedStorageBuffer(int slot, bool write)
{
int mask = 1 << slot;
_usedStorageBuffers |= mask;
if (write)
{
_usedStorageBuffersWrite |= mask;
}
}
public void SetUsedTexture(
Instruction inst,
SamplerType type,
TextureFormat format,
TextureFlags flags,
int cbufSlot,
int handle)
{
inst &= Instruction.Mask;
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore;
bool isWrite = inst == Instruction.ImageStore;
bool accurateType = inst != Instruction.TextureSize && inst != Instruction.Lod;
if (isImage)
{
SetUsedTextureOrImage(_usedImages, cbufSlot, handle, type, format, true, isWrite, false);
}
else
{
SetUsedTextureOrImage(_usedTextures, cbufSlot, handle, type, TextureFormat.Unknown, flags.HasFlag(TextureFlags.IntCoords), false, accurateType);
}
}
private static void SetUsedTextureOrImage(
Dictionary<TextureInfo, TextureMeta> dict,
int cbufSlot,
int handle,
SamplerType type,
TextureFormat format,
bool intCoords,
bool write,
bool accurateType)
{
var dimensions = type.GetDimensions();
var isArray = type.HasFlag(SamplerType.Array);
var isIndexed = type.HasFlag(SamplerType.Indexed);
var usageFlags = TextureUsageFlags.None;
if (intCoords)
{
usageFlags |= TextureUsageFlags.NeedsScaleValue;
var canScale = (dimensions == 2 && !isArray) || (dimensions == 3 && isArray);
if (!canScale)
{
// Resolution scaling cannot be applied to this texture right now.
// Flag so that we know to blacklist scaling on related textures when binding them.
usageFlags |= TextureUsageFlags.ResScaleUnsupported;
}
}
if (write)
{
usageFlags |= TextureUsageFlags.ImageStore;
}
int arraySize = isIndexed ? SamplerArraySize : 1;
for (int layer = 0; layer < arraySize; layer++)
{
var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format);
var meta = new TextureMeta()
{
AccurateType = accurateType,
Type = type,
UsageFlags = usageFlags
};
if (dict.TryGetValue(info, out var existingMeta))
{
meta.UsageFlags |= existingMeta.UsageFlags;
// If the texture we have has inaccurate type information, then
// we prefer the most accurate one.
if (existingMeta.AccurateType)
{
meta.AccurateType = true;
meta.Type = existingMeta.Type;
}
dict[info] = meta;
}
else
{
dict.Add(info, meta);
}
}
}
public BufferDescriptor[] GetConstantBufferDescriptors()
{
if (_cachedConstantBufferDescriptors != null)
{
return _cachedConstantBufferDescriptors;
}
int usedMask = _usedConstantBuffers;
if (UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
{
usedMask = FillMask(usedMask);
}
return _cachedConstantBufferDescriptors = GetBufferDescriptors(usedMask, 0, _counts.IncrementUniformBuffersCount);
}
public BufferDescriptor[] GetStorageBufferDescriptors()
{
return _cachedStorageBufferDescriptors ??= GetBufferDescriptors(FillMask(_usedStorageBuffers), _usedStorageBuffersWrite, _counts.IncrementStorageBuffersCount);
}
private static int FillMask(int mask)
{
// When the storage or uniform buffers are used as array, we must allocate a binding
// even for the "gaps" that are not used on the shader.
// For this reason, fill up the gaps so that all slots up to the highest one are
// marked as "used".
return mask != 0 ? (int)(uint.MaxValue >> BitOperations.LeadingZeroCount((uint)mask)) : 0;
}
private static BufferDescriptor[] GetBufferDescriptors(int usedMask, int writtenMask, Func<int> getBindingCallback)
{
var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)];
for (int i = 0; i < descriptors.Length; i++)
{
int slot = BitOperations.TrailingZeroCount(usedMask);
descriptors[i] = new BufferDescriptor(getBindingCallback(), slot);
if ((writtenMask & (1 << slot)) != 0)
{
descriptors[i].SetFlag(BufferUsageFlags.Write);
}
usedMask &= ~(1 << slot);
}
return descriptors;
}
public TextureDescriptor[] GetTextureDescriptors()
{
return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, _counts.IncrementTexturesCount);
}
public TextureDescriptor[] GetImageDescriptors()
{
return _cachedImageDescriptors ??= GetTextureOrImageDescriptors(_usedImages, _counts.IncrementImagesCount);
}
private static TextureDescriptor[] GetTextureOrImageDescriptors(Dictionary<TextureInfo, TextureMeta> dict, Func<int> getBindingCallback)
{
var descriptors = new TextureDescriptor[dict.Count];
int i = 0;
foreach (var kv in dict.OrderBy(x => x.Key.Indexed).OrderBy(x => x.Key.Handle))
{
var info = kv.Key;
var meta = kv.Value;
int binding = getBindingCallback();
descriptors[i] = new TextureDescriptor(binding, meta.Type, info.Format, info.CbufSlot, info.Handle);
descriptors[i].SetFlag(meta.UsageFlags);
i++;
}
return descriptors;
}
}
}

View file

@ -87,18 +87,16 @@ namespace Ryujinx.Graphics.Shader.Translation
StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
GlslProgram program = GlslGenerator.Generate(sInfo, config);
string glslCode = GlslGenerator.Generate(sInfo, config);
shaderProgramInfo = new ShaderProgramInfo(
program.CBufferDescriptors,
program.SBufferDescriptors,
program.TextureDescriptors,
program.ImageDescriptors,
config.GetConstantBufferDescriptors(),
config.GetStorageBufferDescriptors(),
config.GetTextureDescriptors(),
config.GetImageDescriptors(),
config.UsedFeatures.HasFlag(FeatureFlags.InstanceId),
config.ClipDistancesWritten);
string glslCode = program.Code;
return new ShaderProgram(config.Stage, glslCode);
}
@ -112,7 +110,7 @@ namespace Ryujinx.Graphics.Shader.Translation
Block[][] cfg;
ulong maxEndAddress = 0;
bool hasBindless = false;
bool hasBindless;
if ((flags & TranslationFlags.Compute) != 0)
{