diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 68f4929d..41067a11 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -147,6 +147,13 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Sets a transform feedback buffer on the graphics pipeline. + /// The output from the vertex transformation stages are written into the feedback buffer. + /// + /// Index of the transform feedback buffer + /// Start GPU virtual address of the buffer + /// Size in bytes of the transform feedback buffer public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) { ulong address = TranslateAndCreateBuffer(gpuVa, size); @@ -264,6 +271,25 @@ namespace Ryujinx.Graphics.Gpu.Memory _cpUniformBuffers.EnableMask = mask; } + /// + /// Gets a bit mask indicating which compute uniform buffers are currently bound. + /// + /// Mask where each bit set indicates a bound constant buffer + public uint GetComputeUniformBufferUseMask() + { + uint mask = 0; + + for (int i = 0; i < _cpUniformBuffers.Buffers.Length; i++) + { + if (_cpUniformBuffers.Buffers[i].Address != 0) + { + mask |= 1u << i; + } + } + + return mask; + } + /// /// Sets the enabled uniform buffers mask on the graphics pipeline. /// Each bit set on the mask indicates that the respective buffer index is enabled. @@ -277,6 +303,26 @@ namespace Ryujinx.Graphics.Gpu.Memory _gpUniformBuffersDirty = true; } + /// + /// Gets a bit mask indicating which graphics uniform buffers are currently bound. + /// + /// Index of the shader stage + /// Mask where each bit set indicates a bound constant buffer + public uint GetGraphicsUniformBufferUseMask(int stage) + { + uint mask = 0; + + for (int i = 0; i < _gpUniformBuffers[stage].Buffers.Length; i++) + { + if (_gpUniformBuffers[stage].Buffers[i].Address != 0) + { + mask |= 1u << i; + } + } + + return mask; + } + /// /// Performs address translation of the GPU virtual address, and creates a /// new buffer, if needed, for the specified range. diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 310eee38..6db10c96 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -123,6 +123,17 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shared Memory size in bytes public int QueryComputeSharedMemorySize() => _sharedMemorySize; + /// + /// Queries Constant Buffer usage information. + /// + /// A mask where each bit set indicates a bound constant buffer + public uint QueryConstantBufferUse() + { + return _compute + ? _context.Methods.BufferManager.GetComputeUniformBufferUseMask() + : _context.Methods.BufferManager.GetGraphicsUniformBufferUseMask(_stageIndex); + } + /// /// Queries texture target information. /// diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs index 91ab7ad5..50b9bc9f 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs @@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public ShaderConfig Config { get; } + public bool CbIndexable { get; } + public List CBufferDescriptors { get; } public List SBufferDescriptors { get; } public List TextureDescriptors { get; } @@ -25,9 +27,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private string _indentation; - public CodeGenContext(ShaderConfig config) + public CodeGenContext(ShaderConfig config, bool cbIndexable) { Config = config; + CbIndexable = cbIndexable; CBufferDescriptors = new List(); SBufferDescriptors = new List(); @@ -85,9 +88,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl int cBufSlot = bindless ? operand.CbufSlot : 0; int cBufOffset = bindless ? operand.CbufOffset : 0; - return TextureDescriptors.FindIndex(descriptor => - descriptor.Type == texOp.Type && - descriptor.HandleIndex == texOp.Handle && + return TextureDescriptors.FindIndex(descriptor => + descriptor.Type == texOp.Type && + descriptor.HandleIndex == texOp.Handle && descriptor.CbufSlot == cBufSlot && descriptor.CbufOffset == cBufOffset); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index a7b67a60..cd82aa02 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -213,51 +213,57 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareUniforms(CodeGenContext context, StructuredProgramInfo info) { - foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) + string ubSize = "[" + NumberFormatter.FormatInt(Constants.ConstantBufferSize / 16) + "]"; + + if (info.UsesCbIndexing) { string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); - ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot; + ubName += "_" + DefaultNames.UniformNamePrefix; - context.CBufferDescriptors.Add(new BufferDescriptor(ubName, cbufSlot)); + string blockName = $"{ubName}_{DefaultNames.BlockSuffix}"; - context.AppendLine("layout (std140) uniform " + ubName); + int maxSlot = 0; + + foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) + { + context.CBufferDescriptors.Add(new BufferDescriptor($"{blockName}[{cbufSlot}]", cbufSlot)); + + if (maxSlot < cbufSlot) + { + maxSlot = cbufSlot; + } + } + + context.AppendLine("layout (std140) uniform " + blockName); context.EnterScope(); - string ubSize = "[" + NumberFormatter.FormatInt(Constants.ConstantBufferSize / 16) + "]"; + context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";"); - context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, cbufSlot) + ubSize + ";"); + string arraySize = NumberFormatter.FormatInt(maxSlot + 1); - context.LeaveScope(";"); + context.LeaveScope($" {ubName}[{arraySize}];"); } - } - - private static bool DeclareRenderScale(CodeGenContext context) - { - if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0) + else { - string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage); - - int scaleElements = context.TextureDescriptors.Count; - - if (context.Config.Stage == ShaderStage.Fragment) + foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) { - scaleElements++; // Also includes render target scale, for gl_FragCoord. + string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); + + ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot; + + context.CBufferDescriptors.Add(new BufferDescriptor(ubName, cbufSlot)); + + context.AppendLine("layout (std140) uniform " + ubName); + + context.EnterScope(); + + context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, cbufSlot, false) + ubSize + ";"); + + context.LeaveScope(";"); } - - context.AppendLine($"uniform float {stage}_renderScale[{scaleElements}];"); - - if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling)) - { - context.AppendLine(); - AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl"); - } - - return true; } - - return false; } private static void DeclareStorages(CodeGenContext context, StructuredProgramInfo info) @@ -500,6 +506,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } + private static bool DeclareRenderScale(CodeGenContext context) + { + if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0) + { + string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage); + + int scaleElements = context.TextureDescriptors.Count; + + if (context.Config.Stage == ShaderStage.Fragment) + { + scaleElements++; // Also includes render target scale, for gl_FragCoord. + } + + context.AppendLine($"uniform float {stage}_renderScale[{scaleElements}];"); + + if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling)) + { + context.AppendLine(); + AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl"); + } + + return true; + } + + return false; + } + private static void AppendHelperFunction(CodeGenContext context, string filename) { string code = EmbeddedResources.ReadAllText(filename); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 673fe6a3..00a32262 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { public static GlslProgram Generate(StructuredProgramInfo info, ShaderConfig config) { - CodeGenContext context = new CodeGenContext(config); + CodeGenContext context = new CodeGenContext(config, info.UsesCbIndexing); Declarations.Declare(context, info); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 551fb229..f1c741e6 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else if (node is AstOperand operand) { - return context.OperandManager.GetExpression(operand, context.Config); + return context.OperandManager.GetExpression(operand, context.Config, context.CbIndexable); } throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 8866cd25..cb339f05 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -125,7 +125,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true); - return OperandManager.GetConstantBufferName(src1, offsetExpr, context.Config.Stage); + if (src1 is AstOperand oper && oper.Type == OperandType.Constant) + { + return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, context.Config.Stage, context.CbIndexable); + } + else + { + string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); + + return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, context.Config.Stage); + } } public static string LoadLocal(CodeGenContext context, AstOperation operation) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index e04ce649..d6f30b48 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return name; } - public string GetExpression(AstOperand operand, ShaderConfig config) + public string GetExpression(AstOperand operand, ShaderConfig config, bool cbIndexable) { switch (operand.Type) { @@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return NumberFormatter.FormatInt(operand.Value); case OperandType.ConstantBuffer: - return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage); + return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, cbIndexable); case OperandType.LocalVariable: return _locals[operand]; @@ -115,13 +115,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."); } - public static string GetConstantBufferName(int slot, int offset, ShaderStage stage) + public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable) { - string ubName = GetUbName(stage, slot); - - ubName += "[" + (offset >> 2) + "]"; - - return ubName + "." + GetSwizzleMask(offset & 3); + return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}"; } private static string GetVec4Indexed(string vectorName, string indexExpr) @@ -134,18 +130,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"({result})"; } - public static string GetConstantBufferName(IAstNode slot, string offsetExpr, ShaderStage stage) + public static string GetConstantBufferName(int slot, string offsetExpr, ShaderStage stage, bool cbIndexable) { - // Non-constant slots are not supported. - // It is expected that upstream stages are never going to generate non-constant - // slot access. - AstOperand operand = (AstOperand)slot; + return GetVec4Indexed(GetUbName(stage, slot, cbIndexable) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3"); + } - string ubName = GetUbName(stage, operand.Value); - - string index0 = "[" + offsetExpr + " >> 2]"; - - return GetVec4Indexed(ubName + index0, offsetExpr + " & 3"); + public static string GetConstantBufferName(string slotExpr, string offsetExpr, ShaderStage stage) + { + return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3"); } public static string GetOutAttributeName(AstOperand attr, ShaderConfig config) @@ -228,13 +220,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0"; } - public static string GetUbName(ShaderStage stage, int slot) + public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable) { - string ubName = GetShaderStagePrefix(stage); + if (cbIndexable) + { + return GetUbName(stage, NumberFormatter.FormatInt(slot, VariableType.S32)); + } - ubName += "_" + DefaultNames.UniformNamePrefix + slot; + return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}{slot}_{DefaultNames.UniformNameSuffix}"; + } - return ubName + "_" + 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) diff --git a/Ryujinx.Graphics.Shader/Decoders/CbIndexMode.cs b/Ryujinx.Graphics.Shader/Decoders/CbIndexMode.cs new file mode 100644 index 00000000..4c7a72b5 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/CbIndexMode.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum CbIndexMode + { + Default = 0, + Il = 1, + Is = 2, + Isl = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs index c18a0a9e..05bac28c 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs @@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Offset { get; } public int Slot { get; } + public CbIndexMode IndexMode { get; } public IntegerSize Size { get; } public OpCodeLdc(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) @@ -20,7 +21,8 @@ namespace Ryujinx.Graphics.Shader.Decoders Offset = (opCode.Extract(20, 16) << 16) >> 16; Slot = opCode.Extract(36, 5); - Size = (IntegerSize)opCode.Extract(48, 3); + IndexMode = (CbIndexMode)opCode.Extract(44, 2); + Size = (IntegerSize)opCode.Extract(48, 3); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 59069302..ea936c62 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -39,6 +39,11 @@ return 0xc000; } + public uint QueryConstantBufferUse() + { + return 0xffff; + } + public bool QueryIsTextureBuffer(int handle) { return false; diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 24ba9a06..2a2c8927 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Shader.Instructions res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW)); } } - + if (op.Mode == InterpolationMode.Default) { Operand srcB = GetSrcB(context); @@ -152,7 +152,17 @@ namespace Ryujinx.Graphics.Shader.Instructions int count = op.Size == IntegerSize.B64 ? 2 : 1; - Operand addr = context.IAdd(GetSrcA(context), Const(op.Offset)); + Operand slot = Const(op.Slot); + Operand srcA = GetSrcA(context); + + if (op.IndexMode == CbIndexMode.Is || + op.IndexMode == CbIndexMode.Isl) + { + slot = context.IAdd(slot, context.BitfieldExtractU32(srcA, Const(16), Const(16))); + srcA = context.BitwiseAnd(srcA, Const(0xffff)); + } + + Operand addr = context.IAdd(srcA, Const(op.Offset)); Operand wordOffset = context.ShiftRightU32(addr, Const(2)); @@ -169,7 +179,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand offset = context.IAdd(wordOffset, Const(index)); - Operand value = context.LoadConstant(Const(op.Slot), offset); + Operand value = context.LoadConstant(slot, offset); if (isSmallInt) { diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index f1dd08f2..65de5218 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; +using System.Numerics; namespace Ryujinx.Graphics.Shader.StructuredIr { @@ -73,12 +74,24 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { Operand slot = operation.GetSource(0); - if (slot.Type != OperandType.Constant) + if (slot.Type == OperandType.Constant) { - throw new InvalidOperationException("Found load with non-constant constant buffer slot."); + context.Info.CBuffers.Add(slot.Value); } + else + { + // If the value is not constant, then we don't know + // how many constant buffers are used, so we assume + // all of them are used. + int cbCount = 32 - BitOperations.LeadingZeroCount(context.Config.GpuAccessor.QueryConstantBufferUse()); - context.Info.CBuffers.Add(slot.Value); + for (int index = 0; index < cbCount; index++) + { + context.Info.CBuffers.Add(index); + } + + context.Info.UsesCbIndexing = true; + } } else if (UsesStorage(inst)) { diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index be79f00e..ef3b3eca 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public HashSet OAttributes { get; } public bool UsesInstanceId { get; set; } + public bool UsesCbIndexing { get; set; } public HelperFunctionsMask HelperFunctionsMask { get; set; }