From 402f05b8ef013807997589ecc0a8ff50267dcd23 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 20 May 2023 16:19:26 -0300 Subject: [PATCH] Replace constant buffer access on shader with new Load instruction (#4646) --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- .../Shader/GpuAccessorBase.cs | 19 +- .../CodeGen/Glsl/Declarations.cs | 98 +++------ .../CodeGen/Glsl/DefaultNames.cs | 10 - .../HelperFunctions/TexelFetchScale_cp.glsl | 4 +- .../HelperFunctions/TexelFetchScale_fp.glsl | 4 +- .../HelperFunctions/TexelFetchScale_vp.glsl | 4 +- .../CodeGen/Glsl/Instructions/InstGen.cs | 3 - .../Glsl/Instructions/InstGenHelper.cs | 1 - .../Glsl/Instructions/InstGenMemory.cs | 98 +++++---- .../CodeGen/Glsl/Instructions/IoMap.cs | 3 - .../CodeGen/Glsl/OperandManager.cs | 67 ++---- .../CodeGen/Spirv/CodeGenContext.cs | 30 +-- .../CodeGen/Spirv/Declarations.cs | 129 +++++------- .../CodeGen/Spirv/Instructions.cs | 193 ++++++------------ .../CodeGen/Spirv/ScalingHelpers.cs | 8 +- .../Instructions/InstEmitAttribute.cs | 2 +- .../Instructions/InstEmitMemory.cs | 39 +++- .../IntermediateRepresentation/Instruction.cs | 1 - .../IntermediateRepresentation/IoVariable.cs | 3 - .../StructuredIr/AstOperand.cs | 14 +- .../StructuredIr/BufferDefinition.cs | 20 ++ .../StructuredIr/BufferLayout.cs | 8 + .../StructuredIr/InstructionInfo.cs | 1 - .../StructuredIr/OperandInfo.cs | 1 - .../StructuredIr/ShaderProperties.cs | 21 ++ .../StructuredIr/StructureType.cs | 28 +++ .../StructuredIr/StructuredProgram.cs | 41 ++-- .../StructuredIr/StructuredProgramContext.cs | 32 ++- src/Ryujinx.Graphics.Shader/SupportBuffer.cs | 26 +++ .../Translation/AggregateType.cs | 31 +++ .../Translation/EmitterContext.cs | 6 +- .../Translation/EmitterContextInsts.cs | 46 +++-- .../Translation/FeatureFlags.cs | 1 - .../Optimizations/BindlessToIndexed.cs | 32 ++- .../Optimizations/ConstantFolding.cs | 19 +- .../Optimizations/GlobalToStorage.cs | 24 ++- .../Translation/Optimizations/Optimizer.cs | 8 +- .../Translation/ResourceManager.cs | 126 ++++++++++++ .../Translation/Rewriter.cs | 115 +++++++++-- .../Translation/ShaderConfig.cs | 93 ++------- .../Translation/Translator.cs | 2 +- 42 files changed, 788 insertions(+), 625 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs create mode 100644 src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs create mode 100644 src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs 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); }