diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index 6a81720c..b4764d57 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
- private const uint CodeGenVersion = 5027;
+ private const uint CodeGenVersion = 4646;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index d35b8d92..7db627ba 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
+using System;
namespace Ryujinx.Graphics.Gpu.Shader
{
@@ -16,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly ResourceCounts _resourceCounts;
private readonly int _stageIndex;
+ private readonly int[] _constantBufferBindings;
+
///
/// Creates a new GPU accessor.
///
@@ -25,6 +28,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
_context = context;
_resourceCounts = resourceCounts;
_stageIndex = stageIndex;
+
+ if (context.Capabilities.Api != TargetApi.Vulkan)
+ {
+ _constantBufferBindings = new int[Constants.TotalGpUniformBuffers];
+ _constantBufferBindings.AsSpan().Fill(-1);
+ }
}
public int QueryBindingConstantBuffer(int index)
@@ -36,7 +45,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
else
{
- return _resourceCounts.UniformBuffersCount++;
+ int binding = _constantBufferBindings[index];
+
+ if (binding < 0)
+ {
+ binding = _resourceCounts.UniformBuffersCount++;
+ _constantBufferBindings[index] = binding;
+ }
+
+ return binding;
}
}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 81b79ec4..8d805e32 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -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 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);
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
index 3ab4814c..fc3004a8 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
@@ -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";
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl
index 4ebade5e..08c62548 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl
@@ -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;
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl
index 6c670f91..07a38a7a 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl
@@ -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;
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl
index 19eb119d..72baa441 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_vp.glsl
@@ -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;
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
index 01bd11e5..24ea66d0 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
@@ -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);
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
index 00478f6a..71e40fe7 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
@@ -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);
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index 99519837..ef5260d1 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -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;
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs
index 093ee232..2a73b8b0 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/IoMap.cs
@@ -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),
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
index 92e83358..e34e4e07 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
@@ -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:
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
index ed292ef1..0ef89b39 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
@@ -23,9 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public int InputVertices { get; }
- public Dictionary UniformBuffers { get; } = new Dictionary();
- public Instruction SupportBuffer { get; set; }
- public Instruction UniformBuffersArray { get; set; }
+ public Dictionary ConstantBuffers { get; } = new Dictionary();
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];
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index 821da477..7c242589 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -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 buffers)
{
- if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
+ HashSet decoratedTypes = new HashSet();
+
+ 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);
}
}
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
index 6da8f29d..fda0dc47 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
@@ -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)));
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs
index f6c218c6..c8b21e88 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/ScalingHelpers.cs
@@ -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));
diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
index 1df38761..bb60d274 100644
--- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
+++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
@@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
// FragCoord X/Y must be divided by the render target scale, if resolution scaling is active,
// because the shader code is not expecting scaled values.
- res = context.FPDivide(res, context.Load(StorageKind.Input, IoVariable.SupportBlockRenderScale, null, Const(0)));
+ res = context.FPDivide(res, context.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.RenderScale), Const(0)));
}
else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug())
{
diff --git a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
index c73c6b2a..6f5913eb 100644
--- a/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
+++ b/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
@@ -1,6 +1,7 @@
using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
+using System.Numerics;
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -80,7 +81,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand addr = context.IAdd(srcA, Const(Imm16ToSInt(op.CbufOffset)));
Operand wordOffset = context.ShiftRightU32(addr, Const(2));
- Operand bitOffset = GetBitOffset(context, addr);
for (int index = 0; index < count; index++)
{
@@ -92,11 +92,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
Operand offset = context.IAdd(wordOffset, Const(index));
- Operand value = context.LoadConstant(slot, offset);
+ Operand value = EmitLoadConstant(context, slot, offset);
if (isSmallInt)
{
- value = ExtractSmallInt(context, (LsSize)op.LsSize, bitOffset, value);
+ value = ExtractSmallInt(context, (LsSize)op.LsSize, GetBitOffset(context, addr), value);
}
context.Copy(Register(dest), value);
@@ -154,6 +154,39 @@ namespace Ryujinx.Graphics.Shader.Instructions
EmitStore(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
}
+ private static Operand EmitLoadConstant(EmitterContext context, Operand slot, Operand offset)
+ {
+ Operand vecIndex = context.ShiftRightU32(offset, Const(2));
+ Operand elemIndex = context.BitwiseAnd(offset, Const(3));
+
+ if (slot.Type == OperandType.Constant)
+ {
+ int binding = context.Config.ResourceManager.GetConstantBufferBinding(slot.Value);
+ return context.Load(StorageKind.ConstantBuffer, binding, Const(0), vecIndex, elemIndex);
+ }
+ else
+ {
+ Operand value = Const(0);
+
+ uint cbUseMask = context.Config.GpuAccessor.QueryConstantBufferUse();
+
+ while (cbUseMask != 0)
+ {
+ int cbIndex = BitOperations.TrailingZeroCount(cbUseMask);
+ int binding = context.Config.ResourceManager.GetConstantBufferBinding(cbIndex);
+
+ Operand isCurrent = context.ICompareEqual(slot, Const(cbIndex));
+ Operand currentValue = context.Load(StorageKind.ConstantBuffer, binding, Const(0), vecIndex, elemIndex);
+
+ value = context.ConditionalSelect(isCurrent, currentValue, value);
+
+ cbUseMask &= ~(1u << cbIndex);
+ }
+
+ return value;
+ }
+ }
+
private static Operand EmitAtomicOp(
EmitterContext context,
StorageKind storageKind,
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
index d7c4a961..817755bb 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
@@ -79,7 +79,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
ImageAtomic,
IsNan,
Load,
- LoadConstant,
LoadGlobal,
LoadLocal,
LoadShared,
diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs
index a2163d14..fb9b57bd 100644
--- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs
+++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/IoVariable.cs
@@ -15,7 +15,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
FragmentCoord,
FragmentOutputColor,
FragmentOutputDepth,
- FragmentOutputIsBgra, // TODO: Remove and use constant buffer access.
FrontColorDiffuse,
FrontColorSpecular,
FrontFacing,
@@ -34,8 +33,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
SubgroupLaneId,
SubgroupLeMask,
SubgroupLtMask,
- SupportBlockViewInverse, // TODO: Remove and use constant buffer access.
- SupportBlockRenderScale, // TODO: Remove and use constant buffer access.
TessellationCoord,
TessellationLevelInner,
TessellationLevelOuter,
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs
index 1fc0035f..473aa2e7 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs
@@ -15,9 +15,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public int Value { get; }
- public int CbufSlot { get; }
- public int CbufOffset { get; }
-
private AstOperand()
{
Defs = new HashSet();
@@ -29,16 +26,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public AstOperand(Operand operand) : this()
{
Type = operand.Type;
-
- if (Type == OperandType.ConstantBuffer)
- {
- CbufSlot = operand.GetCbufSlot();
- CbufOffset = operand.GetCbufOffset();
- }
- else
- {
- Value = operand.Value;
- }
+ Value = operand.Value;
}
public AstOperand(OperandType type, int value = 0) : this()
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs
new file mode 100644
index 00000000..5afebc75
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs
@@ -0,0 +1,20 @@
+namespace Ryujinx.Graphics.Shader.StructuredIr
+{
+ readonly struct BufferDefinition
+ {
+ public BufferLayout Layout { get; }
+ public int Set { get; }
+ public int Binding { get; }
+ public string Name { get; }
+ public StructureType Type { get; }
+
+ public BufferDefinition(BufferLayout layout, int set, int binding, string name, StructureType type)
+ {
+ Layout = layout;
+ Set = set;
+ Binding = binding;
+ Name = name;
+ Type = type;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs
new file mode 100644
index 00000000..43a86662
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.Shader.StructuredIr
+{
+ enum BufferLayout
+ {
+ Std140,
+ Std430
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
index 8eccef23..ab813254 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
@@ -90,7 +90,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.ImageAtomic, AggregateType.S32);
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
Add(Instruction.Load, AggregateType.FP32);
- Add(Instruction.LoadConstant, AggregateType.FP32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32);
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
index 38ed1584..48060f6b 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
@@ -24,7 +24,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
OperandType.Argument => AggregateType.S32,
OperandType.Constant => AggregateType.S32,
- OperandType.ConstantBuffer => AggregateType.FP32,
OperandType.Undefined => AggregateType.S32,
_ => throw new ArgumentException($"Invalid operand type \"{type}\".")
};
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
new file mode 100644
index 00000000..061c89ed
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Shader.StructuredIr
+{
+ class ShaderProperties
+ {
+ private readonly Dictionary _constantBuffers;
+
+ public IReadOnlyDictionary ConstantBuffers => _constantBuffers;
+
+ public ShaderProperties()
+ {
+ _constantBuffers = new Dictionary();
+ }
+
+ public void AddConstantBuffer(int binding, BufferDefinition definition)
+ {
+ _constantBuffers[binding] = definition;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs
new file mode 100644
index 00000000..17f49738
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs
@@ -0,0 +1,28 @@
+using Ryujinx.Graphics.Shader.Translation;
+
+namespace Ryujinx.Graphics.Shader.StructuredIr
+{
+ struct StructureField
+ {
+ public AggregateType Type { get; }
+ public string Name { get; }
+ public int ArrayLength { get; }
+
+ public StructureField(AggregateType type, string name, int arrayLength = 1)
+ {
+ Type = type;
+ Name = name;
+ ArrayLength = arrayLength;
+ }
+ }
+
+ class StructureType
+ {
+ public StructureField[] Fields { get; }
+
+ public StructureType(StructureField[] fields)
+ {
+ Fields = fields;
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
index b4ca8ee5..939a52f3 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
@@ -73,27 +73,34 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Instruction inst = operation.Inst;
StorageKind storageKind = operation.StorageKind;
- if ((inst == Instruction.Load || inst == Instruction.Store) && storageKind.IsInputOrOutput())
+ if (inst == Instruction.Load || inst == Instruction.Store)
{
- IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
- bool isOutput = storageKind.IsOutput();
- bool perPatch = storageKind.IsPerPatch();
- int location = 0;
- int component = 0;
-
- if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
+ if (storageKind.IsInputOrOutput())
{
- location = operation.GetSource(1).Value;
+ IoVariable ioVariable = (IoVariable)operation.GetSource(0).Value;
+ bool isOutput = storageKind.IsOutput();
+ bool perPatch = storageKind.IsPerPatch();
+ int location = 0;
+ int component = 0;
- if (operation.SourcesCount > 2 &&
- operation.GetSource(2).Type == OperandType.Constant &&
- context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput))
+ if (context.Config.HasPerLocationInputOrOutput(ioVariable, isOutput))
{
- component = operation.GetSource(2).Value;
- }
- }
+ location = operation.GetSource(1).Value;
- context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component));
+ if (operation.SourcesCount > 2 &&
+ operation.GetSource(2).Type == OperandType.Constant &&
+ context.Config.HasPerLocationInputOrOutputComponent(ioVariable, location, operation.GetSource(2).Value, isOutput))
+ {
+ component = operation.GetSource(2).Value;
+ }
+ }
+
+ context.Info.IoDefinitions.Add(new IoDefinition(storageKind, ioVariable, location, component));
+ }
+ else if (storageKind == StorageKind.ConstantBuffer && operation.GetSource(0).Type == OperandType.Constant)
+ {
+ context.Config.ResourceManager.SetUsedConstantBufferBinding(operation.GetSource(0).Value);
+ }
}
bool vectorDest = IsVectorDestInst(inst);
@@ -105,7 +112,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
for (int index = 0; index < operation.SourcesCount; index++)
{
- sources[index] = context.GetOperand(operation.GetSource(index));
+ sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index));
}
for (int index = 0; index < outDestsCount; index++)
diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
index 68bbdeb1..c5ad3683 100644
--- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
+++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
@@ -298,6 +298,33 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
return newTemp;
}
+ public IAstNode GetOperandOrCbLoad(Operand operand)
+ {
+ if (operand.Type == OperandType.ConstantBuffer)
+ {
+ int cbufSlot = operand.GetCbufSlot();
+ int cbufOffset = operand.GetCbufOffset();
+
+ int binding = Config.ResourceManager.GetConstantBufferBinding(cbufSlot);
+ int vecIndex = cbufOffset >> 2;
+ int elemIndex = cbufOffset & 3;
+
+ Config.ResourceManager.SetUsedConstantBufferBinding(binding);
+
+ IAstNode[] sources = new IAstNode[]
+ {
+ new AstOperand(OperandType.Constant, binding),
+ new AstOperand(OperandType.Constant, 0),
+ new AstOperand(OperandType.Constant, vecIndex),
+ new AstOperand(OperandType.Constant, elemIndex)
+ };
+
+ return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, sources, sources.Length);
+ }
+
+ return GetOperand(operand);
+ }
+
public AstOperand GetOperand(Operand operand)
{
if (operand == null)
@@ -307,11 +334,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
if (operand.Type != OperandType.LocalVariable)
{
- if (operand.Type == OperandType.ConstantBuffer)
- {
- Config.SetUsedConstantBuffer(operand.GetCbufSlot());
- }
-
return new AstOperand(operand);
}
diff --git a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs
index 5fe99327..5eb7fe46 100644
--- a/src/Ryujinx.Graphics.Shader/SupportBuffer.cs
+++ b/src/Ryujinx.Graphics.Shader/SupportBuffer.cs
@@ -1,4 +1,6 @@
using Ryujinx.Common.Memory;
+using Ryujinx.Graphics.Shader.StructuredIr;
+using Ryujinx.Graphics.Shader.Translation;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Shader
@@ -11,8 +13,20 @@ namespace Ryujinx.Graphics.Shader
public T W;
}
+ enum SupportBufferField
+ {
+ // Must match the order of the fields on the struct.
+ FragmentAlphaTest,
+ FragmentIsBgra,
+ ViewportInverse,
+ FragmentRenderScaleCount,
+ RenderScale
+ }
+
public struct SupportBuffer
{
+ internal const int Binding = 0;
+
public static int FieldSize;
public static int RequiredSize;
@@ -47,6 +61,18 @@ namespace Ryujinx.Graphics.Shader
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
}
+ internal static StructureType GetStructureType()
+ {
+ return new StructureType(new[]
+ {
+ new StructureField(AggregateType.U32, "s_alpha_test"),
+ new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount),
+ new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_inverse"),
+ new StructureField(AggregateType.S32, "s_frag_scale_count"),
+ new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount)
+ });
+ }
+
public Vector4 FragmentAlphaTest;
public Array8> FragmentIsBgra;
public Vector4 ViewportInverse;
diff --git a/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs
index 24993e00..b1b40f65 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/AggregateType.cs
@@ -22,4 +22,35 @@
Array = 1 << 10
}
+
+ static class AggregateTypeExtensions
+ {
+ public static int GetSizeInBytes(this AggregateType type)
+ {
+ int elementSize = (type & AggregateType.ElementTypeMask) switch
+ {
+ AggregateType.Bool or
+ AggregateType.FP32 or
+ AggregateType.S32 or
+ AggregateType.U32 => 4,
+ AggregateType.FP64 => 8,
+ _ => 0
+ };
+
+ switch (type & AggregateType.ElementCountMask)
+ {
+ case AggregateType.Vector2:
+ elementSize *= 2;
+ break;
+ case AggregateType.Vector3:
+ elementSize *= 3;
+ break;
+ case AggregateType.Vector4:
+ elementSize *= 4;
+ break;
+ }
+
+ return elementSize;
+ }
+ }
}
diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
index 112baccf..0c51b16f 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
@@ -234,8 +234,8 @@ namespace Ryujinx.Graphics.Shader.Translation
{
Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0));
Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1));
- Operand xScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(0));
- Operand yScale = this.Load(StorageKind.Input, IoVariable.SupportBlockViewInverse, null, Const(1));
+ Operand xScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(0));
+ Operand yScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(1));
Operand negativeOne = ConstF(-1.0f);
this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
@@ -420,7 +420,7 @@ namespace Ryujinx.Graphics.Shader.Translation
// Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL).
if (!supportsBgra && (component == 0 || component == 2))
{
- Operand isBgra = this.Load(StorageKind.Input, IoVariable.FragmentOutputIsBgra, null, Const(rtIndex));
+ Operand isBgra = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.FragmentIsBgra), Const(rtIndex));
Operand lblIsBgra = Label();
Operand lblEnd = Label();
diff --git a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
index 93748249..e41a28f1 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
@@ -549,11 +549,31 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(fpType | Instruction.IsNan, Local(), a);
}
+ public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding)
+ {
+ return context.Add(Instruction.Load, storageKind, Local(), Const(binding));
+ }
+
+ public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0)
+ {
+ return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0);
+ }
+
+ public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1)
+ {
+ return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0, e1);
+ }
+
+ public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand e2)
+ {
+ return context.Add(Instruction.Load, storageKind, Local(), Const(binding), e0, e1, e2);
+ }
+
public static Operand Load(this EmitterContext context, StorageKind storageKind, IoVariable ioVariable, Operand primVertex = null)
{
return primVertex != null
- ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex)
- : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable));
+ ? context.Load(storageKind, (int)ioVariable, primVertex)
+ : context.Load(storageKind, (int)ioVariable);
}
public static Operand Load(
@@ -564,8 +584,8 @@ namespace Ryujinx.Graphics.Shader.Translation
Operand elemIndex)
{
return primVertex != null
- ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, elemIndex)
- : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), elemIndex);
+ ? context.Load(storageKind, (int)ioVariable, primVertex, elemIndex)
+ : context.Load(storageKind, (int)ioVariable, elemIndex);
}
public static Operand Load(
@@ -577,22 +597,8 @@ namespace Ryujinx.Graphics.Shader.Translation
Operand elemIndex)
{
return primVertex != null
- ? context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), primVertex, arrayIndex, elemIndex)
- : context.Add(Instruction.Load, storageKind, Local(), Const((int)ioVariable), arrayIndex, elemIndex);
- }
-
- 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);
+ ? context.Load(storageKind, (int)ioVariable, primVertex, arrayIndex, elemIndex)
+ : context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex);
}
public static Operand LoadGlobal(this EmitterContext context, Operand a, Operand b)
diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
index c035f212..e55ed13d 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
@@ -19,7 +19,6 @@ namespace Ryujinx.Graphics.Shader.Translation
InstanceId = 1 << 3,
DrawParameters = 1 << 4,
RtLayer = 1 << 5,
- CbIndexing = 1 << 6,
IaIndexing = 1 << 7,
OaIndexing = 1 << 8,
FixedFuncAttr = 1 << 9
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs
index ca46a1f5..9a3ae1b8 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs
@@ -32,25 +32,49 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue;
}
- if (handleAsgOp.Inst != Instruction.LoadConstant)
+ if (handleAsgOp.Inst != Instruction.Load ||
+ handleAsgOp.StorageKind != StorageKind.ConstantBuffer ||
+ handleAsgOp.SourcesCount != 4)
{
continue;
}
Operand ldcSrc0 = handleAsgOp.GetSource(0);
+
+ if (ldcSrc0.Type != OperandType.Constant ||
+ !config.ResourceManager.TryGetConstantBufferSlot(ldcSrc0.Value, out int src0CbufSlot) ||
+ src0CbufSlot != 2)
+ {
+ continue;
+ }
+
Operand ldcSrc1 = handleAsgOp.GetSource(1);
- if (ldcSrc0.Type != OperandType.Constant || ldcSrc0.Value != 2)
+ // We expect field index 0 to be accessed.
+ if (ldcSrc1.Type != OperandType.Constant || ldcSrc1.Value != 0)
{
continue;
}
- if (!(ldcSrc1.AsgOp is Operation shrOp) || shrOp.Inst != Instruction.ShiftRightU32)
+ Operand ldcSrc2 = handleAsgOp.GetSource(2);
+
+ // FIXME: This is missing some checks, for example, a check to ensure that the shift value is 2.
+ // Might be not worth fixing since if that doesn't kick in, the result will be no texture
+ // to access anyway which is also wrong.
+ // Plus this whole transform is fundamentally flawed as-is since we have no way to know the array size.
+ // Eventually, this should be entirely removed in favor of a implementation that supports true bindless
+ // texture access.
+ if (!(ldcSrc2.AsgOp is Operation shrOp) || shrOp.Inst != Instruction.ShiftRightU32)
{
continue;
}
- if (!(shrOp.GetSource(0).AsgOp is Operation addOp) || addOp.Inst != Instruction.Add)
+ if (!(shrOp.GetSource(0).AsgOp is Operation shrOp2) || shrOp2.Inst != Instruction.ShiftRightU32)
+ {
+ continue;
+ }
+
+ if (!(shrOp2.GetSource(0).AsgOp is Operation addOp) || addOp.Inst != Instruction.Add)
{
continue;
}
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs
index 6729f077..4caadb73 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class ConstantFolding
{
- public static void RunPass(Operation operation)
+ public static void RunPass(ShaderConfig config, Operation operation)
{
if (!AreAllSourcesConstant(operation))
{
@@ -153,8 +153,21 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
EvaluateFPUnary(operation, (x) => float.IsNaN(x));
break;
- case Instruction.LoadConstant:
- operation.TurnIntoCopy(Cbuf(operation.GetSource(0).Value, operation.GetSource(1).Value));
+ case Instruction.Load:
+ if (operation.StorageKind == StorageKind.ConstantBuffer && operation.SourcesCount == 4)
+ {
+ int binding = operation.GetSource(0).Value;
+ int fieldIndex = operation.GetSource(1).Value;
+
+ if (config.ResourceManager.TryGetConstantBufferSlot(binding, out int cbufSlot) && fieldIndex == 0)
+ {
+ int vecIndex = operation.GetSource(2).Value;
+ int elemIndex = operation.GetSource(3).Value;
+ int cbufOffset = vecIndex * 4 + elemIndex;
+
+ operation.TurnIntoCopy(Cbuf(cbufSlot, cbufOffset));
+ }
+ }
break;
case Instruction.Maximum:
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
index 774d3e36..7758b4c6 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs
@@ -347,21 +347,23 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return wordOffset;
}
- Operand[] sources = new Operand[operation.SourcesCount];
+ Operand cbufOffset = GetCbufOffset();
+ Operand vecIndex = Local();
+ Operand elemIndex = Local();
+
+ node.List.AddBefore(node, new Operation(Instruction.ShiftRightU32, 0, vecIndex, cbufOffset, Const(2)));
+ node.List.AddBefore(node, new Operation(Instruction.BitwiseAnd, 0, elemIndex, cbufOffset, Const(3)));
+
+ Operand[] sources = new Operand[4];
int cbSlot = UbeFirstCbuf + storageIndex;
- sources[0] = Const(cbSlot);
- sources[1] = GetCbufOffset();
+ sources[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot));
+ sources[1] = Const(0);
+ sources[2] = vecIndex;
+ sources[3] = elemIndex;
- config.SetUsedConstantBuffer(cbSlot);
-
- for (int index = 2; index < operation.SourcesCount; index++)
- {
- sources[index] = operation.GetSource(index);
- }
-
- Operation ldcOp = new Operation(Instruction.LoadConstant, operation.Dest, sources);
+ Operation ldcOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, operation.Dest, sources);
for (int index = 0; index < operation.SourcesCount; index++)
{
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
index b41e47e4..b126e2c4 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
@@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
{
- RunOptimizationPasses(blocks);
+ RunOptimizationPasses(blocks, config);
int sbUseMask = 0;
int ubeUseMask = 0;
@@ -31,10 +31,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
// Run optimizations one last time to remove any code that is now optimizable after above passes.
- RunOptimizationPasses(blocks);
+ RunOptimizationPasses(blocks, config);
}
- private static void RunOptimizationPasses(BasicBlock[] blocks)
+ private static void RunOptimizationPasses(BasicBlock[] blocks, ShaderConfig config)
{
bool modified;
@@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue;
}
- ConstantFolding.RunPass(operation);
+ ConstantFolding.RunPass(config, operation);
Simplification.RunPass(operation);
if (DestIsLocalVar(operation))
diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
new file mode 100644
index 00000000..b31790d3
--- /dev/null
+++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
@@ -0,0 +1,126 @@
+using Ryujinx.Graphics.Shader.StructuredIr;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace Ryujinx.Graphics.Shader.Translation
+{
+ class ResourceManager
+ {
+ private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
+
+ private readonly IGpuAccessor _gpuAccessor;
+ private readonly ShaderProperties _properties;
+ private readonly string _stagePrefix;
+
+ private readonly int[] _cbSlotToBindingMap;
+
+ private readonly HashSet _usedConstantBufferBindings;
+
+ public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
+ {
+ _gpuAccessor = gpuAccessor;
+ _properties = properties;
+ _stagePrefix = GetShaderStagePrefix(stage);
+
+ _cbSlotToBindingMap = new int[18];
+ _cbSlotToBindingMap.AsSpan().Fill(-1);
+
+ _usedConstantBufferBindings = new HashSet();
+
+ properties.AddConstantBuffer(0, new BufferDefinition(BufferLayout.Std140, 0, 0, "support_buffer", SupportBuffer.GetStructureType()));
+ }
+
+ public int GetConstantBufferBinding(int slot)
+ {
+ int binding = _cbSlotToBindingMap[slot];
+ if (binding < 0)
+ {
+ binding = _gpuAccessor.QueryBindingConstantBuffer(slot);
+ _cbSlotToBindingMap[slot] = binding;
+ string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
+ AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}");
+ }
+
+ return binding;
+ }
+
+ public bool TryGetConstantBufferSlot(int binding, out int slot)
+ {
+ for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
+ {
+ if (_cbSlotToBindingMap[slot] == binding)
+ {
+ return true;
+ }
+ }
+
+ slot = 0;
+ return false;
+ }
+
+ public void SetUsedConstantBufferBinding(int binding)
+ {
+ _usedConstantBufferBindings.Add(binding);
+ }
+
+ public BufferDescriptor[] GetConstantBufferDescriptors()
+ {
+ var descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count];
+
+ int descriptorIndex = 0;
+
+ for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
+ {
+ int binding = _cbSlotToBindingMap[slot];
+
+ if (binding >= 0 && _usedConstantBufferBindings.Contains(binding))
+ {
+ descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot);
+ }
+ }
+
+ if (descriptors.Length != descriptorIndex)
+ {
+ Array.Resize(ref descriptors, descriptorIndex);
+ }
+
+ return descriptors;
+ }
+
+ private void AddNewConstantBuffer(int binding, string name)
+ {
+ StructureType type = new StructureType(new[]
+ {
+ new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16)
+ });
+
+ _properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
+ }
+
+ public void InheritFrom(ResourceManager other)
+ {
+ for (int i = 0; i < other._cbSlotToBindingMap.Length; i++)
+ {
+ int binding = other._cbSlotToBindingMap[i];
+
+ if (binding >= 0)
+ {
+ _cbSlotToBindingMap[i] = binding;
+ }
+ }
+ }
+
+ public static string GetShaderStagePrefix(ShaderStage stage)
+ {
+ uint index = (uint)stage;
+
+ if (index >= _stagePrefixes.Length)
+ {
+ return "invalid";
+ }
+
+ return _stagePrefixes[index];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
index 8167efc1..711661c9 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
@@ -1,7 +1,6 @@
-using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.StructuredIr;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Numerics;
@@ -16,6 +15,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
bool isVertexShader = config.Stage == ShaderStage.Vertex;
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
+ bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug();
bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat();
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
@@ -45,6 +45,11 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
+ if (hasVectorIndexingBug)
+ {
+ InsertVectorComponentSelect(node, config);
+ }
+
LinkedListNode nextNode = node.Next;
if (operation is TextureOperation texOp)
@@ -71,6 +76,84 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
+ private static void InsertVectorComponentSelect(LinkedListNode node, ShaderConfig config)
+ {
+ Operation operation = (Operation)node.Value;
+
+ if (operation.Inst != Instruction.Load ||
+ operation.StorageKind != StorageKind.ConstantBuffer ||
+ operation.SourcesCount < 3)
+ {
+ return;
+ }
+
+ Operand bindingIndex = operation.GetSource(0);
+ Operand fieldIndex = operation.GetSource(1);
+ Operand elemIndex = operation.GetSource(operation.SourcesCount - 1);
+
+ if (bindingIndex.Type != OperandType.Constant ||
+ fieldIndex.Type != OperandType.Constant ||
+ elemIndex.Type == OperandType.Constant)
+ {
+ return;
+ }
+
+ BufferDefinition buffer = config.Properties.ConstantBuffers[bindingIndex.Value];
+ StructureField field = buffer.Type.Fields[fieldIndex.Value];
+
+ int elemCount = (field.Type & AggregateType.ElementCountMask) switch
+ {
+ AggregateType.Vector2 => 2,
+ AggregateType.Vector3 => 3,
+ AggregateType.Vector4 => 4,
+ _ => 1
+ };
+
+ if (elemCount == 1)
+ {
+ return;
+ }
+
+ Operand result = null;
+
+ for (int i = 0; i < elemCount; i++)
+ {
+ Operand value = Local();
+ Operand[] inputs = new Operand[operation.SourcesCount];
+
+ for (int srcIndex = 0; srcIndex < inputs.Length - 1; srcIndex++)
+ {
+ inputs[srcIndex] = operation.GetSource(srcIndex);
+ }
+
+ inputs[inputs.Length - 1] = Const(i);
+
+ Operation loadOp = new Operation(Instruction.Load, StorageKind.ConstantBuffer, value, inputs);
+
+ node.List.AddBefore(node, loadOp);
+
+ if (i == 0)
+ {
+ result = value;
+ }
+ else
+ {
+ Operand isCurrentIndex = Local();
+ Operand selection = Local();
+
+ Operation compareOp = new Operation(Instruction.CompareEqual, isCurrentIndex, new Operand[] { elemIndex, Const(i) });
+ Operation selectOp = new Operation(Instruction.ConditionalSelect, selection, new Operand[] { isCurrentIndex, value, result });
+
+ node.List.AddBefore(node, compareOp);
+ node.List.AddBefore(node, selectOp);
+
+ result = selection;
+ }
+ }
+
+ operation.TurnIntoCopy(result);
+ }
+
private static LinkedListNode RewriteGlobalAccess(LinkedListNode node, ShaderConfig config)
{
Operation operation = (Operation)node.Value;
@@ -90,6 +173,15 @@ namespace Ryujinx.Graphics.Shader.Translation
return local;
}
+ Operand PrependStorageOperation(Instruction inst, StorageKind storageKind, params Operand[] sources)
+ {
+ Operand local = Local();
+
+ node.List.AddBefore(node, new Operation(inst, storageKind, local, sources));
+
+ return local;
+ }
+
Operand PrependExistingOperation(Operation operation)
{
Operand local = Local();
@@ -204,8 +296,6 @@ namespace Ryujinx.Graphics.Shader.Translation
cbeUseMask &= ~(1 << slot);
- config.SetUsedConstantBuffer(cbSlot);
-
Operand previousResult = PrependExistingOperation(storageOp);
int cbOffset = GetConstantUbeOffset(slot);
@@ -216,18 +306,17 @@ namespace Ryujinx.Graphics.Shader.Translation
Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst);
Operand cbIndex = PrependOperation(Instruction.ShiftRightU32, byteOffsetConst, Const(2));
+ Operand vecIndex = PrependOperation(Instruction.ShiftRightU32, cbIndex, Const(2));
+ Operand elemIndex = PrependOperation(Instruction.BitwiseAnd, cbIndex, Const(3));
- Operand[] sourcesCb = new Operand[operation.SourcesCount];
+ Operand[] sourcesCb = new Operand[4];
- sourcesCb[0] = Const(cbSlot);
- sourcesCb[1] = cbIndex;
+ sourcesCb[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot));
+ sourcesCb[1] = Const(0);
+ sourcesCb[2] = vecIndex;
+ sourcesCb[3] = elemIndex;
- for (int index = 2; index < operation.SourcesCount; index++)
- {
- sourcesCb[index] = operation.GetSource(index);
- }
-
- Operand ldcResult = PrependOperation(Instruction.LoadConstant, sourcesCb);
+ Operand ldcResult = PrependStorageOperation(Instruction.Load, StorageKind.ConstantBuffer, sourcesCb);
storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult);
}
diff --git a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index ae60bcc6..77560797 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -39,6 +39,10 @@ namespace Ryujinx.Graphics.Shader.Translation
public TranslationOptions Options { get; }
+ public ShaderProperties Properties { get; }
+
+ public ResourceManager ResourceManager { get; }
+
public bool TransformFeedbackEnabled { get; }
private TransformFeedbackOutput[] _transformFeedbackOutputs;
@@ -109,7 +113,6 @@ namespace Ryujinx.Graphics.Shader.Translation
public int AccessibleStorageBuffersMask { get; private set; }
public int AccessibleConstantBuffersMask { get; private set; }
- private int _usedConstantBuffers;
private int _usedStorageBuffers;
private int _usedStorageBuffersWrite;
@@ -128,20 +131,17 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly Dictionary _sbSlots;
private readonly Dictionary _sbSlotsReverse;
- private BufferDescriptor[] _cachedConstantBufferDescriptors;
private BufferDescriptor[] _cachedStorageBufferDescriptors;
private TextureDescriptor[] _cachedTextureDescriptors;
private TextureDescriptor[] _cachedImageDescriptors;
- private int _firstConstantBufferBinding;
private int _firstStorageBufferBinding;
- public int FirstConstantBufferBinding => _firstConstantBufferBinding;
public int FirstStorageBufferBinding => _firstStorageBufferBinding;
- public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options)
+ public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options)
{
- Stage = ShaderStage.Compute;
+ Stage = stage;
GpuAccessor = gpuAccessor;
Options = options;
@@ -158,6 +158,9 @@ namespace Ryujinx.Graphics.Shader.Translation
_sbSlots = new Dictionary();
_sbSlotsReverse = new Dictionary();
+
+ Properties = new ShaderProperties();
+ ResourceManager = new ResourceManager(stage, gpuAccessor, Properties);
}
public ShaderConfig(
@@ -165,9 +168,8 @@ namespace Ryujinx.Graphics.Shader.Translation
OutputTopology outputTopology,
int maxOutputVertices,
IGpuAccessor gpuAccessor,
- TranslationOptions options) : this(gpuAccessor, options)
+ TranslationOptions options) : this(stage, gpuAccessor, options)
{
- Stage = stage;
ThreadsPerInputPrimitive = 1;
OutputTopology = outputTopology;
MaxOutputVertices = maxOutputVertices;
@@ -179,9 +181,8 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
- public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(gpuAccessor, options)
+ public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options)
{
- Stage = header.Stage;
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive;
OutputTopology = header.OutputTopology;
@@ -428,12 +429,13 @@ namespace Ryujinx.Graphics.Shader.Translation
public void InheritFrom(ShaderConfig other)
{
+ ResourceManager.InheritFrom(other.ResourceManager);
+
ClipDistancesWritten |= other.ClipDistancesWritten;
UsedFeatures |= other.UsedFeatures;
UsedInputAttributes |= other.UsedInputAttributes;
UsedOutputAttributes |= other.UsedOutputAttributes;
- _usedConstantBuffers |= other._usedConstantBuffers;
_usedStorageBuffers |= other._usedStorageBuffers;
_usedStorageBuffersWrite |= other._usedStorageBuffersWrite;
@@ -641,11 +643,6 @@ namespace Ryujinx.Graphics.Shader.Translation
AccessibleConstantBuffersMask = ubeMask;
}
- public void SetUsedConstantBuffer(int slot)
- {
- _usedConstantBuffers |= 1 << slot;
- }
-
public void SetUsedStorageBuffer(int slot, bool write)
{
int mask = 1 << slot;
@@ -762,27 +759,6 @@ namespace Ryujinx.Graphics.Shader.Translation
return meta;
}
- public BufferDescriptor[] GetConstantBufferDescriptors()
- {
- if (_cachedConstantBufferDescriptors != null)
- {
- return _cachedConstantBufferDescriptors;
- }
-
- int usedMask = _usedConstantBuffers;
-
- if (UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
- {
- usedMask |= (int)GpuAccessor.QueryConstantBufferUse();
- }
-
- return _cachedConstantBufferDescriptors = GetUniformBufferDescriptors(
- usedMask,
- UsedFeatures.HasFlag(FeatureFlags.CbIndexing),
- out _firstConstantBufferBinding,
- GpuAccessor.QueryBindingConstantBuffer);
- }
-
public BufferDescriptor[] GetStorageBufferDescriptors()
{
if (_cachedStorageBufferDescriptors != null)
@@ -798,47 +774,6 @@ namespace Ryujinx.Graphics.Shader.Translation
GpuAccessor.QueryBindingStorageBuffer);
}
- private static BufferDescriptor[] GetUniformBufferDescriptors(int usedMask, bool isArray, out int firstBinding, Func getBindingCallback)
- {
- firstBinding = 0;
- int lastSlot = -1;
- bool hasFirstBinding = false;
- var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)];
-
- for (int i = 0; i < descriptors.Length; i++)
- {
- int slot = BitOperations.TrailingZeroCount(usedMask);
-
- if (isArray)
- {
- // The next array entries also consumes bindings, even if they are unused.
- for (int j = lastSlot + 1; j < slot; j++)
- {
- int binding = getBindingCallback(j);
-
- if (!hasFirstBinding)
- {
- firstBinding = binding;
- hasFirstBinding = true;
- }
- }
- }
-
- lastSlot = slot;
- descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot);
-
- if (!hasFirstBinding)
- {
- firstBinding = descriptors[i].Binding;
- hasFirstBinding = true;
- }
-
- usedMask &= ~(1 << slot);
- }
-
- return descriptors;
- }
-
private BufferDescriptor[] GetStorageBufferDescriptors(
int usedMask,
int writtenMask,
@@ -1009,7 +944,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
{
return new ShaderProgramInfo(
- GetConstantBufferDescriptors(),
+ ResourceManager.GetConstantBufferDescriptors(),
GetStorageBufferDescriptors(),
GetTextureDescriptors(),
GetImageDescriptors(),
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs
index 77d3b568..87d97e52 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (options.Flags.HasFlag(TranslationFlags.Compute))
{
- config = new ShaderConfig(gpuAccessor, options);
+ config = new ShaderConfig(ShaderStage.Compute, gpuAccessor, options);
program = Decoder.Decode(config, address);
}