Replace constant buffer access on shader with new Load instruction (#4646)

This commit is contained in:
gdkchan 2023-05-20 16:19:26 -03:00 committed by GitHub
parent fb27042e01
commit 402f05b8ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 788 additions and 625 deletions

View file

@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
@ -102,13 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine();
}
var cBufferDescriptors = context.Config.GetConstantBufferDescriptors();
if (cBufferDescriptors.Length != 0)
{
DeclareUniforms(context, cBufferDescriptors);
context.AppendLine();
}
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
if (sBufferDescriptors.Length != 0)
@ -265,18 +260,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
scaleElements++; // Also includes render target scale, for gl_FragCoord.
}
DeclareSupportUniformBlock(context, context.Config.Stage, scaleElements);
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0)
{
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
context.AppendLine();
}
}
else if (isFragment || context.Config.Stage == ShaderStage.Vertex)
{
DeclareSupportUniformBlock(context, context.Config.Stage, 0);
}
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
@ -389,36 +378,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
};
}
private static void DeclareUniforms(CodeGenContext context, BufferDescriptor[] descriptors)
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
{
string ubSize = "[" + NumberFormatter.FormatInt(Constants.ConstantBufferSize / 16) + "]";
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
foreach (BufferDefinition buffer in buffers)
{
string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
ubName += "_" + DefaultNames.UniformNamePrefix;
string blockName = $"{ubName}_{DefaultNames.BlockSuffix}";
context.AppendLine($"layout (binding = {context.Config.FirstConstantBufferBinding}, std140) uniform {blockName}");
context.EnterScope();
context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";");
context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];");
}
else
{
foreach (var descriptor in descriptors)
string layout = buffer.Layout switch
{
string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
BufferLayout.Std140 => "std140",
_ => "std430"
};
ubName += "_" + DefaultNames.UniformNamePrefix + descriptor.Slot;
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}");
context.EnterScope();
context.AppendLine($"layout (binding = {descriptor.Binding}, std140) uniform {ubName}");
context.EnterScope();
context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, descriptor.Slot, false) + ubSize + ";");
context.LeaveScope(";");
foreach (StructureField field in buffer.Type.Fields)
{
if (field.Type.HasFlag(AggregateType.Array))
{
string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array);
string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
}
else
{
string typeName = GetVarTypeName(context, field.Type);
context.AppendLine($"{typeName} {field.Name};");
}
}
context.LeaveScope($" {buffer.Name};");
context.AppendLine();
}
}
@ -759,39 +750,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine($"layout (location = {location}) patch out vec4 {name};");
}
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
{
bool needsSupportBlock = stage == ShaderStage.Fragment ||
(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable());
if (!needsSupportBlock && scaleElements == 0)
{
return;
}
context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}");
context.EnterScope();
switch (stage)
{
case ShaderStage.Fragment:
case ShaderStage.Vertex:
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
context.AppendLine($"vec4 {DefaultNames.SupportBlockViewportInverse};");
context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};");
break;
case ShaderStage.Compute:
context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];");
break;
}
context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{SupportBuffer.RenderScaleMaxCount}];");
context.LeaveScope(";");
context.AppendLine();
}
private static void AppendHelperFunction(CodeGenContext context, string filename)
{
string code = EmbeddedResources.ReadAllText(filename);

View file

@ -15,18 +15,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string DataName = "data";
public const string SupportBlockName = "support_block";
public const string SupportBlockAlphaTestName = "s_alpha_test";
public const string SupportBlockIsBgraName = "s_is_bgra";
public const string SupportBlockViewportInverse = "s_viewport_inverse";
public const string SupportBlockFragmentScaleCount = "s_frag_scale_count";
public const string SupportBlockRenderScaleName = "s_render_scale";
public const string BlockSuffix = "block";
public const string UniformNamePrefix = "c";
public const string UniformNameSuffix = "data";
public const string LocalMemoryName = "local_mem";
public const string SharedMemoryName = "shared_mem";

View file

@ -1,6 +1,6 @@
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
{
float scale = s_render_scale[samplerIndex];
float scale = support_buffer.s_render_scale[1 + samplerIndex];
if (scale == 1.0)
{
return inputVec;
@ -10,7 +10,7 @@
int Helper_TextureSizeUnscale(int size, int samplerIndex)
{
float scale = s_render_scale[samplerIndex];
float scale = support_buffer.s_render_scale[1 + samplerIndex];
if (scale == 1.0)
{
return size;

View file

@ -1,6 +1,6 @@
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
{
float scale = s_render_scale[1 + samplerIndex];
float scale = support_buffer.s_render_scale[1 + samplerIndex];
if (scale == 1.0)
{
return inputVec;
@ -17,7 +17,7 @@
int Helper_TextureSizeUnscale(int size, int samplerIndex)
{
float scale = abs(s_render_scale[1 + samplerIndex]);
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex]);
if (scale == 1.0)
{
return size;

View file

@ -1,6 +1,6 @@
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
{
float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]);
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]);
if (scale == 1.0)
{
return inputVec;
@ -11,7 +11,7 @@
int Helper_TextureSizeUnscale(int size, int samplerIndex)
{
float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]);
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]);
if (scale == 1.0)
{
return size;

View file

@ -167,9 +167,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.Load:
return Load(context, operation);
case Instruction.LoadConstant:
return LoadConstant(context, operation);
case Instruction.LoadLocal:
return LoadLocal(context, operation);

View file

@ -83,7 +83,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.ImageAtomic, InstType.Special);
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
Add(Instruction.Load, InstType.Special);
Add(Instruction.LoadConstant, InstType.Special);
Add(Instruction.LoadLocal, InstType.Special);
Add(Instruction.LoadShared, InstType.Special);
Add(Instruction.LoadStorage, InstType.Special);

View file

@ -215,29 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return GenerateLoadOrStore(context, operation, isStore: false);
}
public static string LoadConstant(CodeGenContext context, AstOperation operation)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true);
var config = context.Config;
bool indexElement = !config.GpuAccessor.QueryHostHasVectorIndexingBug();
if (src1 is AstOperand operand && operand.Type == OperandType.Constant)
{
bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing);
return OperandManager.GetConstantBufferName(operand.Value, offsetExpr, config.Stage, cbIndexable, indexElement);
}
else
{
string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, config.Stage, indexElement);
}
}
public static string LoadLocal(CodeGenContext context, AstOperation operation)
{
return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
@ -809,9 +786,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string varName;
AggregateType varType;
int srcIndex = 0;
int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount;
switch (storageKind)
{
case StorageKind.ConstantBuffer:
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
}
int binding = bindingIndex.Value;
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[binding];
if (!(operation.GetSource(srcIndex++) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
}
StructureField field = buffer.Type.Fields[fieldIndex.Value];
varName = $"{buffer.Name}.{field.Name}";
varType = field.Type;
break;
case StorageKind.Input:
case StorageKind.InputPerPatch:
case StorageKind.Output:
@ -864,40 +861,39 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
varName = $"gl_out[{expr}].{varName}";
}
}
int firstSrcIndex = srcIndex;
int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount;
for (; srcIndex < inputsCount; srcIndex++)
{
IAstNode src = operation.GetSource(srcIndex);
if ((varType & AggregateType.ElementCountMask) != 0 &&
srcIndex == inputsCount - 1 &&
src is AstOperand elementIndex &&
elementIndex.Type == OperandType.Constant)
{
varName += "." + "xyzw"[elementIndex.Value & 3];
}
else if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output)
{
// GLSL requires that for tessellation control shader outputs,
// that the index expression must be *exactly* "gl_InvocationID",
// otherwise the compilation fails.
// TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR.
varName += "[gl_InvocationID]";
}
else
{
varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]";
}
}
break;
default:
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
}
int firstSrcIndex = srcIndex;
for (; srcIndex < inputsCount; srcIndex++)
{
IAstNode src = operation.GetSource(srcIndex);
if ((varType & AggregateType.ElementCountMask) != 0 &&
srcIndex == inputsCount - 1 &&
src is AstOperand elementIndex &&
elementIndex.Type == OperandType.Constant)
{
varName += "." + "xyzw"[elementIndex.Value & 3];
}
else if (srcIndex == firstSrcIndex && context.Config.Stage == ShaderStage.TessellationControl && storageKind == StorageKind.Output)
{
// GLSL requires that for tessellation control shader outputs,
// that the index expression must be *exactly* "gl_InvocationID",
// otherwise the compilation fails.
// TODO: Get rid of this and use expression propagation to make sure we generate the correct code from IR.
varName += "[gl_InvocationID]";
}
else
{
varName += $"[{GetSoureExpr(context, src, AggregateType.S32)}]";
}
}
if (isStore)
{
varType &= AggregateType.ElementTypeMask;

View file

@ -27,7 +27,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
IoVariable.FragmentCoord => ("gl_FragCoord", AggregateType.Vector4 | AggregateType.FP32),
IoVariable.FragmentOutputColor => GetFragmentOutputColorVariableName(config, location),
IoVariable.FragmentOutputDepth => ("gl_FragDepth", AggregateType.FP32),
IoVariable.FragmentOutputIsBgra => (DefaultNames.SupportBlockIsBgraName, AggregateType.Array | AggregateType.Bool),
IoVariable.FrontColorDiffuse => ("gl_FrontColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
IoVariable.FrontColorSpecular => ("gl_FrontSecondaryColor", AggregateType.Vector4 | AggregateType.FP32), // Deprecated.
IoVariable.FrontFacing => ("gl_FrontFacing", AggregateType.Bool),
@ -46,8 +45,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
IoVariable.SubgroupLaneId => GetSubgroupInvocationIdVariableName(config),
IoVariable.SubgroupLeMask => GetSubgroupMaskVariableName(config, "Le"),
IoVariable.SubgroupLtMask => GetSubgroupMaskVariableName(config, "Lt"),
IoVariable.SupportBlockRenderScale => (DefaultNames.SupportBlockRenderScaleName, AggregateType.Array | AggregateType.FP32),
IoVariable.SupportBlockViewInverse => (DefaultNames.SupportBlockViewportInverse, AggregateType.Vector2 | AggregateType.FP32),
IoVariable.TessellationCoord => ("gl_TessCoord", AggregateType.Vector3 | AggregateType.FP32),
IoVariable.TessellationLevelInner => ("gl_TessLevelInner", AggregateType.Array | AggregateType.FP32),
IoVariable.TessellationLevelOuter => ("gl_TessLevelOuter", AggregateType.Array | AggregateType.FP32),

View file

@ -36,63 +36,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
OperandType.Argument => GetArgumentName(operand.Value),
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
OperandType.LocalVariable => _locals[operand],
OperandType.Undefined => DefaultNames.UndefinedName,
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
};
}
private static string GetConstantBufferName(AstOperand operand, ShaderConfig config)
{
return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing));
}
public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable)
{
return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}";
}
private static string GetVec4Indexed(string vectorName, string indexExpr, bool indexElement)
{
if (indexElement)
{
return $"{vectorName}[{indexExpr}]";
}
string result = $"{vectorName}.x";
for (int i = 1; i < 4; i++)
{
result = $"(({indexExpr}) == {i}) ? ({vectorName}.{GetSwizzleMask(i)}) : ({result})";
}
return $"({result})";
}
public static string GetConstantBufferName(int slot, string offsetExpr, ShaderStage stage, bool cbIndexable, bool indexElement)
{
return GetVec4Indexed(GetUbName(stage, slot, cbIndexable) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
}
public static string GetConstantBufferName(string slotExpr, string offsetExpr, ShaderStage stage, bool indexElement)
{
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
}
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
{
if (cbIndexable)
{
return GetUbName(stage, NumberFormatter.FormatInt(slot, AggregateType.S32));
}
return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}{slot}_{DefaultNames.UniformNameSuffix}";
}
private static string GetUbName(ShaderStage stage, string slotExpr)
{
return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}[{slotExpr}].{DefaultNames.DataName}";
}
public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
{
return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr);
@ -168,6 +117,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
switch (operation.StorageKind)
{
case StorageKind.ConstantBuffer:
if (!(operation.GetSource(0) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
}
if (!(operation.GetSource(1) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
}
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value];
StructureField field = buffer.Type.Fields[fieldIndex.Value];
return field.Type & AggregateType.ElementTypeMask;
case StorageKind.Input:
case StorageKind.InputPerPatch:
case StorageKind.Output:

View file

@ -23,9 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public int InputVertices { get; }
public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>();
public Instruction SupportBuffer { get; set; }
public Instruction UniformBuffersArray { get; set; }
public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
public Instruction StorageBuffersArray { get; set; }
public Instruction LocalMemory { get; set; }
public Instruction SharedMemory { get; set; }
@ -217,7 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
IrOperandType.Argument => GetArgument(type, operand),
IrOperandType.Constant => GetConstant(type, operand),
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
IrOperandType.LocalVariable => GetLocal(type, operand),
IrOperandType.Undefined => GetUndefined(type),
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
@ -274,31 +271,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
};
}
public Instruction GetConstantBuffer(AggregateType type, AstOperand operand)
{
var i1 = Constant(TypeS32(), 0);
var i2 = Constant(TypeS32(), operand.CbufOffset >> 2);
var i3 = Constant(TypeU32(), operand.CbufOffset & 3);
Instruction elemPointer;
if (UniformBuffersArray != null)
{
var ubVariable = UniformBuffersArray;
var i0 = Constant(TypeS32(), operand.CbufSlot);
elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i0, i1, i2, i3);
}
else
{
var ubVariable = UniformBuffers[operand.CbufSlot];
elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i1, i2, i3);
}
return BitcastIfNeeded(type, AggregateType.FP32, Load(TypeFP32(), elemPointer));
}
public Instruction GetLocalPointer(AstOperand local)
{
return _locals[local];

View file

@ -98,8 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
DeclareLocalMemory(context, localMemorySize);
}
DeclareSupportBuffer(context);
DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
DeclareSamplers(context, context.Config.GetTextureDescriptors());
DeclareImages(context, context.Config.GetImageDescriptors());
@ -127,84 +126,63 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return variable;
}
private static void DeclareSupportBuffer(CodeGenContext context)
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
{
if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>();
foreach (BufferDefinition buffer in buffers)
{
return;
}
int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4;
int alignmentMask = alignment - 1;
int offset = 0;
var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
SpvInstruction[] structFieldTypes = new SpvInstruction[buffer.Type.Fields.Length];
int[] structFieldOffsets = new int[buffer.Type.Fields.Length];
context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
for (int fieldIndex = 0; fieldIndex < buffer.Type.Fields.Length; fieldIndex++)
{
StructureField field = buffer.Type.Fields[fieldIndex];
int fieldSize = (field.Type.GetSizeInBytes() + alignmentMask) & ~alignmentMask;
var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, viewportInverseVectorType, context.TypeS32(), renderScaleArrayType);
structFieldTypes[fieldIndex] = context.GetType(field.Type, field.ArrayLength);
structFieldOffsets[fieldIndex] = offset;
context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset);
context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset);
context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
context.Decorate(supportBufferStructType, Decoration.Block);
if (field.Type.HasFlag(AggregateType.Array))
{
// We can't decorate the type more than once.
if (decoratedTypes.Add(structFieldTypes[fieldIndex]))
{
context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
}
var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform);
offset += fieldSize * field.ArrayLength;
}
else
{
offset += fieldSize;
}
}
context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0);
context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0);
var ubStructType = context.TypeStruct(false, structFieldTypes);
context.AddGlobalVariable(supportBufferVariable);
if (decoratedTypes.Add(ubStructType))
{
context.Decorate(ubStructType, Decoration.Block);
context.SupportBuffer = supportBufferVariable;
}
for (int fieldIndex = 0; fieldIndex < structFieldOffsets.Length; fieldIndex++)
{
context.MemberDecorate(ubStructType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]);
}
}
private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
{
if (descriptors.Length == 0)
{
return;
}
uint ubSize = Constants.ConstantBufferSize / 16;
var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true);
context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16);
var ubStructType = context.TypeStruct(true, ubArrayType);
context.Decorate(ubStructType, Decoration.Block);
context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0);
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
{
int count = descriptors.Max(x => x.Slot) + 1;
var ubStructArrayType = context.TypeArray(ubStructType, context.Constant(context.TypeU32(), count));
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructArrayType);
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u");
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding);
context.Name(ubVariable, buffer.Name);
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding);
context.AddGlobalVariable(ubVariable);
context.UniformBuffersArray = ubVariable;
}
else
{
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
foreach (var descriptor in descriptors)
{
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}");
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
context.AddGlobalVariable(ubVariable);
context.UniformBuffers.Add(descriptor.Slot, ubVariable);
}
context.ConstantBuffers.Add(buffer.Binding, ubVariable);
}
}
@ -394,25 +372,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
foreach (var ioDefinition in info.IoDefinitions)
{
var ioVariable = ioDefinition.IoVariable;
// Those are actually from constant buffer, rather than being actual inputs or outputs,
// so we must ignore them here as they are declared as part of the support buffer.
// TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input").
if (ioVariable == IoVariable.FragmentOutputIsBgra ||
ioVariable == IoVariable.SupportBlockRenderScale ||
ioVariable == IoVariable.SupportBlockViewInverse)
{
continue;
}
bool isOutput = ioDefinition.StorageKind.IsOutput();
bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
PixelImap iq = PixelImap.Unused;
if (context.Config.Stage == ShaderStage.Fragment)
{
var ioVariable = ioDefinition.IoVariable;
if (ioVariable == IoVariable.UserDefined)
{
iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType();
@ -429,6 +393,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
}
bool isOutput = ioDefinition.StorageKind.IsOutput();
bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq);
}
}

View file

@ -98,7 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.ImageStore, GenerateImageStore);
Add(Instruction.IsNan, GenerateIsNan);
Add(Instruction.Load, GenerateLoad);
Add(Instruction.LoadConstant, GenerateLoadConstant);
Add(Instruction.LoadLocal, GenerateLoadLocal);
Add(Instruction.LoadShared, GenerateLoadShared);
Add(Instruction.LoadStorage, GenerateLoadStorage);
@ -313,10 +312,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
for (int i = 0; i < args.Length; i++)
{
var operand = (AstOperand)operation.GetSource(i + 1);
var operand = operation.GetSource(i + 1);
if (i >= function.InArguments.Length)
{
args[i] = context.GetLocalPointer(operand);
args[i] = context.GetLocalPointer((AstOperand)operand);
}
else
{
@ -867,68 +867,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return GenerateLoadOrStore(context, operation, isStore: false);
}
private static OperationResult GenerateLoadConstant(CodeGenContext context, AstOperation operation)
{
var src1 = operation.GetSource(0);
var src2 = context.Get(AggregateType.S32, operation.GetSource(1));
var i1 = context.Constant(context.TypeS32(), 0);
var i2 = context.ShiftRightArithmetic(context.TypeS32(), src2, context.Constant(context.TypeS32(), 2));
var i3 = context.BitwiseAnd(context.TypeS32(), src2, context.Constant(context.TypeS32(), 3));
SpvInstruction value = null;
if (context.Config.GpuAccessor.QueryHostHasVectorIndexingBug())
{
// Test for each component individually.
for (int i = 0; i < 4; i++)
{
var component = context.Constant(context.TypeS32(), i);
SpvInstruction elemPointer;
if (context.UniformBuffersArray != null)
{
var ubVariable = context.UniformBuffersArray;
var i0 = context.Get(AggregateType.S32, src1);
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i0, i1, i2, component);
}
else
{
var ubVariable = context.UniformBuffers[((AstOperand)src1).Value];
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i1, i2, component);
}
SpvInstruction newValue = context.Load(context.TypeFP32(), elemPointer);
value = value != null ? context.Select(context.TypeFP32(), context.IEqual(context.TypeBool(), i3, component), newValue, value) : newValue;
}
}
else
{
SpvInstruction elemPointer;
if (context.UniformBuffersArray != null)
{
var ubVariable = context.UniformBuffersArray;
var i0 = context.Get(AggregateType.S32, src1);
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i0, i1, i2, i3);
}
else
{
var ubVariable = context.UniformBuffers[((AstOperand)src1).Value];
elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeFP32()), ubVariable, i1, i2, i3);
}
value = context.Load(context.TypeFP32(), elemPointer);
}
return new OperationResult(AggregateType.FP32, value);
}
private static OperationResult GenerateLoadLocal(CodeGenContext context, AstOperation operation)
{
return GenerateLoadLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);
@ -1990,12 +1928,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
StorageKind storageKind = operation.StorageKind;
SpvInstruction pointer;
StorageClass storageClass;
SpvInstruction baseObj;
AggregateType varType;
int srcIndex = 0;
switch (storageKind)
{
case StorageKind.ConstantBuffer:
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
}
if (!(operation.GetSource(srcIndex) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
{
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
}
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value];
StructureField field = buffer.Type.Fields[fieldIndex.Value];
storageClass = StorageClass.Uniform;
varType = field.Type & AggregateType.ElementTypeMask;
baseObj = context.ConstantBuffers[bindingIndex.Value];
break;
case StorageKind.Input:
case StorageKind.InputPerPatch:
case StorageKind.Output:
@ -2038,33 +1996,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
varType = context.Config.GetFragmentOutputColorType(location);
}
else if (ioVariable == IoVariable.FragmentOutputIsBgra)
{
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeU32());
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 1), elemIndex);
varType = AggregateType.U32;
break;
}
else if (ioVariable == IoVariable.SupportBlockRenderScale)
{
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 4), elemIndex);
varType = AggregateType.FP32;
break;
}
else if (ioVariable == IoVariable.SupportBlockViewInverse)
{
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
var elemIndex = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(pointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 2), elemIndex);
varType = AggregateType.FP32;
break;
}
else
{
(_, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
@ -2072,55 +2003,57 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
varType &= AggregateType.ElementTypeMask;
int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
var storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
var ioDefinition = new IoDefinition(storageKind, ioVariable, location, component);
var dict = isPerPatch
? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
: (isOutput ? context.Outputs : context.Inputs);
SpvInstruction baseObj = dict[ioDefinition];
SpvInstruction e0, e1, e2;
switch (inputsCount)
{
case 0:
pointer = baseObj;
break;
case 1:
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0);
break;
case 2:
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1);
break;
case 3:
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2);
break;
default:
var indexes = new SpvInstruction[inputsCount];
int index = 0;
for (; index < inputsCount; srcIndex++, index++)
{
indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex));
}
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes);
break;
}
baseObj = dict[ioDefinition];
break;
default:
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
}
int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
SpvInstruction e0, e1, e2;
SpvInstruction pointer;
switch (inputsCount)
{
case 0:
pointer = baseObj;
break;
case 1:
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0);
break;
case 2:
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1);
break;
case 3:
e0 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
e1 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
e2 = context.Get(AggregateType.S32, operation.GetSource(srcIndex++));
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, e0, e1, e2);
break;
default:
var indexes = new SpvInstruction[inputsCount];
int index = 0;
for (; index < inputsCount; srcIndex++, index++)
{
indexes[index] = context.Get(AggregateType.S32, operation.GetSource(srcIndex));
}
pointer = context.AccessChain(context.TypePointer(storageClass, context.GetType(varType)), baseObj, indexes);
break;
}
if (isStore)
{
context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex)));

View file

@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (context.Config.Stage == ShaderStage.Vertex)
{
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3));
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex);
var scale = context.Load(context.TypeFP32(), scaleElemPointer);
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
@ -201,7 +201,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (context.Config.Stage == ShaderStage.Vertex)
{
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3));
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
@ -209,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex);
var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer));
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));