diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index f6dcf0526..867092776 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 2261; + private const ulong ShaderCodeGenVersion = 2290; // Progress reporting helpers private volatile int _shaderCount; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs index e20df384e..f0f8ea351 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs @@ -1,7 +1,5 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; -using System.Collections.Generic; using System.Text; namespace Ryujinx.Graphics.Shader.CodeGen.Glsl @@ -10,22 +8,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { public const string Tab = " "; - private readonly StructuredProgramInfo _info; - public StructuredFunction CurrentFunction { get; set; } public ShaderConfig Config { get; } - public bool CbIndexable => _info.UsesCbIndexing; - - public List CBufferDescriptors { get; } - public List SBufferDescriptors { get; } - public List TextureDescriptors { get; } - public List ImageDescriptors { get; } - public OperandManager OperandManager { get; } - private StringBuilder _sb; + private readonly StructuredProgramInfo _info; + + private readonly StringBuilder _sb; private int _level; @@ -36,11 +27,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl _info = info; Config = config; - CBufferDescriptors = new List(); - SBufferDescriptors = new List(); - TextureDescriptors = new List(); - ImageDescriptors = new List(); - OperandManager = new OperandManager(); _sb = new StringBuilder(); @@ -84,23 +70,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl AppendLine("}" + suffix); } - private int FindDescriptorIndex(List list, AstTextureOperation texOp) + private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp) { - return list.FindIndex(descriptor => - descriptor.Type == texOp.Type && - descriptor.CbufSlot == texOp.CbufSlot && - descriptor.HandleIndex == texOp.Handle && - descriptor.Format == texOp.Format); + for (int i = 0; i < array.Length; i++) + { + var descriptor = array[i]; + + if (descriptor.Type == texOp.Type && + descriptor.CbufSlot == texOp.CbufSlot && + descriptor.HandleIndex == texOp.Handle && + descriptor.Format == texOp.Format) + { + return i; + } + } + + return -1; } public int FindTextureDescriptorIndex(AstTextureOperation texOp) { - return FindDescriptorIndex(TextureDescriptors, texOp); + return FindDescriptorIndex(Config.GetTextureDescriptors(), texOp); } public int FindImageDescriptorIndex(AstTextureOperation texOp) { - return FindDescriptorIndex(ImageDescriptors, texOp); + return FindDescriptorIndex(Config.GetImageDescriptors(), texOp); } public StructuredFunction GetFunction(int id) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 94487da00..6e67b6829 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -70,30 +70,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); } - if (info.CBuffers.Count != 0) + var cBufferDescriptors = context.Config.GetConstantBufferDescriptors(); + if (cBufferDescriptors.Length != 0) { - DeclareUniforms(context, info); + DeclareUniforms(context, cBufferDescriptors); context.AppendLine(); } - if (info.SBuffers.Count != 0) + var sBufferDescriptors = context.Config.GetStorageBufferDescriptors(); + if (sBufferDescriptors.Length != 0) { - DeclareStorages(context, info); + DeclareStorages(context, sBufferDescriptors); context.AppendLine(); } - if (info.Samplers.Count != 0) + var textureDescriptors = context.Config.GetTextureDescriptors(); + if (textureDescriptors.Length != 0) { - DeclareSamplers(context, info); + DeclareSamplers(context, textureDescriptors); context.AppendLine(); } - if (info.Images.Count != 0) + var imageDescriptors = context.Config.GetImageDescriptors(); + if (imageDescriptors.Length != 0) { - DeclareImages(context, info); + DeclareImages(context, imageDescriptors); context.AppendLine(); } @@ -246,58 +250,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl throw new ArgumentException($"Invalid variable type \"{type}\"."); } - private static void DeclareUniforms(CodeGenContext context, StructuredProgramInfo info) + private static void DeclareUniforms(CodeGenContext context, BufferDescriptor[] descriptors) { string ubSize = "[" + NumberFormatter.FormatInt(Constants.ConstantBufferSize / 16) + "]"; - if (info.UsesCbIndexing) + if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)) { - int count = info.CBuffers.Max() + 1; - - int[] bindings = new int[count]; - - for (int i = 0; i < count; i++) - { - bindings[i] = context.Config.Counts.IncrementUniformBuffersCount(); - } - - foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) - { - context.CBufferDescriptors.Add(new BufferDescriptor(bindings[cbufSlot], cbufSlot)); - } - string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); ubName += "_" + DefaultNames.UniformNamePrefix; string blockName = $"{ubName}_{DefaultNames.BlockSuffix}"; - context.AppendLine($"layout (binding = {bindings[0]}, std140) uniform {blockName}"); + context.AppendLine($"layout (binding = {descriptors[0].Binding}, std140) uniform {blockName}"); context.EnterScope(); context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";"); - context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(count)}];"); + context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(descriptors.Length)}];"); } else { - foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) + foreach (var descriptor in descriptors) { - int binding = context.Config.Counts.IncrementUniformBuffersCount(); - - context.CBufferDescriptors.Add(new BufferDescriptor(binding, cbufSlot)); - string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); - ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot; + ubName += "_" + DefaultNames.UniformNamePrefix + descriptor.Slot; - context.AppendLine($"layout (binding = {binding}, std140) uniform {ubName}"); + context.AppendLine($"layout (binding = {descriptor.Binding}, std140) uniform {ubName}"); context.EnterScope(); - context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, cbufSlot, false) + ubSize + ";"); + context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, descriptor.Slot, false) + ubSize + ";"); context.LeaveScope(";"); } } } - private static void DeclareStorages(CodeGenContext context, StructuredProgramInfo info) + private static void DeclareStorages(CodeGenContext context, BufferDescriptor[] descriptors) { string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage); @@ -305,130 +291,81 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string blockName = $"{sbName}_{DefaultNames.BlockSuffix}"; - int count = info.SBuffers.Max() + 1; - - int[] bindings = new int[count]; - - for (int i = 0; i < count; i++) - { - bindings[i] = context.Config.Counts.IncrementStorageBuffersCount(); - } - - foreach (int sbufSlot in info.SBuffers) - { - context.SBufferDescriptors.Add(new BufferDescriptor(bindings[sbufSlot], sbufSlot)); - } - - context.AppendLine($"layout (binding = {bindings[0]}, std430) buffer {blockName}"); + context.AppendLine($"layout (binding = {descriptors[0].Binding}, std430) buffer {blockName}"); context.EnterScope(); context.AppendLine("uint " + DefaultNames.DataName + "[];"); - context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(count)}];"); + context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(descriptors.Length)}];"); } - private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo info) + private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors) { - HashSet samplers = new HashSet(); - - // Texture instructions other than TextureSample (like TextureSize) - // may have incomplete sampler type information. In those cases, - // we prefer instead the more accurate information from the - // TextureSample instruction, if both are available. - foreach (AstTextureOperation texOp in info.Samplers.OrderBy(x => x.Handle * 2 + (x.Inst == Instruction.TextureSample ? 0 : 1))) + int arraySize = 0; + foreach (var descriptor in descriptors) { - string indexExpr = NumberFormatter.FormatInt(texOp.ArraySize); - - string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); - - if ((texOp.Flags & TextureFlags.Bindless) != 0 || !samplers.Add(samplerName)) + if (descriptor.Type.HasFlag(SamplerType.Indexed)) { - continue; - } - - int firstBinding = -1; - - if ((texOp.Type & SamplerType.Indexed) != 0) - { - for (int index = 0; index < texOp.ArraySize; index++) + if (arraySize == 0) { - int binding = context.Config.Counts.IncrementTexturesCount(); - - if (firstBinding < 0) - { - firstBinding = binding; - } - - var desc = new TextureDescriptor(binding, texOp.Type, texOp.Format, texOp.CbufSlot, texOp.Handle + index * 2); - - context.TextureDescriptors.Add(desc); + arraySize = ShaderConfig.SamplerArraySize; + } + else if (--arraySize != 0) + { + continue; } } - else - { - firstBinding = context.Config.Counts.IncrementTexturesCount(); - var desc = new TextureDescriptor(firstBinding, texOp.Type, texOp.Format, texOp.CbufSlot, texOp.Handle); + string indexExpr = NumberFormatter.FormatInt(arraySize); - context.TextureDescriptors.Add(desc); - } + string samplerName = OperandManager.GetSamplerName( + context.Config.Stage, + descriptor.CbufSlot, + descriptor.HandleIndex, + descriptor.Type.HasFlag(SamplerType.Indexed), + indexExpr); - string samplerTypeName = texOp.Type.ToGlslSamplerType(); + string samplerTypeName = descriptor.Type.ToGlslSamplerType(); - context.AppendLine($"layout (binding = {firstBinding}) uniform {samplerTypeName} {samplerName};"); + context.AppendLine($"layout (binding = {descriptor.Binding}) uniform {samplerTypeName} {samplerName};"); } } - private static void DeclareImages(CodeGenContext context, StructuredProgramInfo info) + private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors) { - HashSet images = new HashSet(); - - foreach (AstTextureOperation texOp in info.Images.OrderBy(x => x.Handle)) + int arraySize = 0; + foreach (var descriptor in descriptors) { - string indexExpr = NumberFormatter.FormatInt(texOp.ArraySize); - - string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr); - - if ((texOp.Flags & TextureFlags.Bindless) != 0 || !images.Add(imageName)) + if (descriptor.Type.HasFlag(SamplerType.Indexed)) { - continue; - } - - int firstBinding = -1; - - if ((texOp.Type & SamplerType.Indexed) != 0) - { - for (int index = 0; index < texOp.ArraySize; index++) + if (arraySize == 0) { - int binding = context.Config.Counts.IncrementImagesCount(); - - if (firstBinding < 0) - { - firstBinding = binding; - } - - var desc = new TextureDescriptor(binding, texOp.Type, texOp.Format, texOp.CbufSlot, texOp.Handle + index * 2); - - context.ImageDescriptors.Add(desc); + arraySize = ShaderConfig.SamplerArraySize; + } + else if (--arraySize != 0) + { + continue; } } - else - { - firstBinding = context.Config.Counts.IncrementImagesCount(); - var desc = new TextureDescriptor(firstBinding, texOp.Type, texOp.Format, texOp.CbufSlot, texOp.Handle); + string indexExpr = NumberFormatter.FormatInt(arraySize); - context.ImageDescriptors.Add(desc); - } + string imageName = OperandManager.GetImageName( + context.Config.Stage, + descriptor.CbufSlot, + descriptor.HandleIndex, + descriptor.Format, + descriptor.Type.HasFlag(SamplerType.Indexed), + indexExpr); - string layout = texOp.Format.ToGlslFormat(); + string layout = descriptor.Format.ToGlslFormat(); if (!string.IsNullOrEmpty(layout)) { layout = ", " + layout; } - string imageTypeName = texOp.Type.ToGlslImageType(texOp.Format.GetComponentType()); + string imageTypeName = descriptor.Type.ToGlslImageType(descriptor.Format.GetComponentType()); - context.AppendLine($"layout (binding = {firstBinding}{layout}) uniform {imageTypeName} {imageName};"); + context.AppendLine($"layout (binding = {descriptor.Binding}{layout}) uniform {imageTypeName} {imageName};"); } } @@ -528,7 +465,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage); - int scaleElements = context.TextureDescriptors.Count + context.ImageDescriptors.Count; + int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length; if (context.Config.Stage == ShaderStage.Fragment) { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 37a1cd89c..c430a21a6 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 { private const string MainFunctionName = "main"; - public static GlslProgram Generate(StructuredProgramInfo info, ShaderConfig config) + public static string Generate(StructuredProgramInfo info, ShaderConfig config) { CodeGenContext context = new CodeGenContext(info, config); @@ -37,12 +37,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl PrintFunction(context, info, info.Functions[0], MainFunctionName); - return new GlslProgram( - context.CBufferDescriptors.ToArray(), - context.SBufferDescriptors.ToArray(), - context.TextureDescriptors.ToArray(), - context.ImageDescriptors.ToArray(), - context.GetCode()); + return context.GetCode(); } private static void PrintFunction(CodeGenContext context, StructuredProgramInfo info, StructuredFunction function, string funcName = null) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs deleted file mode 100644 index 31b7f3126..000000000 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Glsl -{ - class GlslProgram - { - public BufferDescriptor[] CBufferDescriptors { get; } - public BufferDescriptor[] SBufferDescriptors { get; } - public TextureDescriptor[] TextureDescriptors { get; } - public TextureDescriptor[] ImageDescriptors { get; } - - public string Code { get; } - - public GlslProgram( - BufferDescriptor[] cBufferDescriptors, - BufferDescriptor[] sBufferDescriptors, - TextureDescriptor[] textureDescriptors, - TextureDescriptor[] imageDescriptors, - string code) - { - CBufferDescriptors = cBufferDescriptors; - SBufferDescriptors = sBufferDescriptors; - TextureDescriptors = textureDescriptors; - ImageDescriptors = imageDescriptors; - Code = code; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 0ea7f1512..d5cd0f72e 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else if (node is AstOperand operand) { - return context.OperandManager.GetExpression(operand, context.Config, context.CbIndexable); + return context.OperandManager.GetExpression(operand, context.Config); } throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions switch (memRegion) { case Instruction.MrShared: args += LoadShared(context, operation); break; - case Instruction.MrStorage: args += LoadStorage(context, operation, forAtomic: true); break; + case Instruction.MrStorage: args += LoadStorage(context, operation); break; default: throw new InvalidOperationException($"Invalid memory region \"{memRegion}\"."); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 273aaef8f..911c7b056 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -56,7 +56,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string ApplyScaling(string vector) { int index = context.FindImageDescriptorIndex(texOp); - TextureUsageFlags flags = TextureUsageFlags.NeedsScaleValue; if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) && texOp.Inst == Instruction.ImageLoad && @@ -64,7 +63,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions !isIndexed) { // Image scales start after texture ones. - int scaleIndex = context.TextureDescriptors.Count + index; + int scaleIndex = context.Config.GetTextureDescriptors().Length + index; if (pCount == 3 && isArray) { @@ -75,19 +74,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { vector = "Helper_TexelFetchScale(" + vector + ", " + scaleIndex + ")"; } - else - { - flags |= TextureUsageFlags.ResScaleUnsupported; - } - } - else - { - flags |= TextureUsageFlags.ResScaleUnsupported; - } - - if (!isBindless) - { - context.ImageDescriptors[index] = context.ImageDescriptors[index].SetFlag(flags); } return vector; @@ -112,7 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (texOp.Inst == Instruction.ImageStore) { int texIndex = context.FindImageDescriptorIndex(texOp); - context.ImageDescriptors[texIndex] = context.ImageDescriptors[texIndex].SetFlag(TextureUsageFlags.ImageStore); VariableType type = texOp.Format.GetComponentType(); @@ -176,12 +161,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (src1 is AstOperand oper && oper.Type == OperandType.Constant) { - return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, context.Config.Stage, context.CbIndexable); + bool cbIndexable = context.Config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing); + return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, context.Config.Stage, cbIndexable); } else { string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, context.Config.Stage); } } @@ -205,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $"{arrayName}[{offsetExpr}]"; } - public static string LoadStorage(CodeGenContext context, AstOperation operation, bool forAtomic = false) + public static string LoadStorage(CodeGenContext context, AstOperation operation) { IAstNode src1 = operation.GetSource(0); IAstNode src2 = operation.GetSource(1); @@ -213,11 +198,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - if (forAtomic) - { - SetStorageWriteFlag(context, src1, context.Config.Stage); - } - return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); } @@ -306,7 +286,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.U32); - SetStorageWriteFlag(context, src1, context.Config.Stage); string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); return $"{sb} = {src}"; @@ -471,7 +450,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (intCoords) { int index = context.FindTextureDescriptorIndex(texOp); - TextureUsageFlags flags = TextureUsageFlags.NeedsScaleValue; if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) && !isBindless && @@ -486,22 +464,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { vector = "Helper_TexelFetchScale(" + vector + ", " + index + ")"; } - else - { - flags |= TextureUsageFlags.ResScaleUnsupported; - } - } - else - { - // Resolution scaling cannot be applied to this texture right now. - // Flag so that we know to blacklist scaling on related textures when binding them. - - flags |= TextureUsageFlags.ResScaleUnsupported; - } - - if (!isBindless) - { - context.TextureDescriptors[index] = context.TextureDescriptors[index].SetFlag(flags); } } @@ -638,32 +600,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } - private static void SetStorageWriteFlag(CodeGenContext context, IAstNode indexExpr, ShaderStage stage) - { - // Attempt to find a BufferDescriptor with the given index. - // If it cannot be resolved or is not constant, assume that the slot expression could potentially index any of them, - // and set the flag on all storage buffers. - - int index = -1; - - if (indexExpr is AstOperand operand && operand.Type == OperandType.Constant) - { - index = context.SBufferDescriptors.FindIndex(buffer => buffer.Slot == operand.Value); - } - - if (index != -1) - { - context.SBufferDescriptors[index] = context.SBufferDescriptors[index].SetFlag(BufferUsageFlags.Write); - } - else - { - for (int i = 0; i < context.SBufferDescriptors.Count; i++) - { - context.SBufferDescriptors[i] = context.SBufferDescriptors[i].SetFlag(BufferUsageFlags.Write); - } - } - } - private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage) { string sbName = OperandManager.GetShaderStagePrefix(stage); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 61f200827..9e79a0807 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -94,30 +94,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return name; } - public string GetExpression(AstOperand operand, ShaderConfig config, bool cbIndexable) + public string GetExpression(AstOperand operand, ShaderConfig config) { - switch (operand.Type) + return operand.Type switch { - case OperandType.Argument: - return GetArgumentName(operand.Value); - - case OperandType.Attribute: - return GetAttributeName(operand, config); - - case OperandType.Constant: - return NumberFormatter.FormatInt(operand.Value); - - case OperandType.ConstantBuffer: - return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, cbIndexable); - - case OperandType.LocalVariable: - return _locals[operand]; - - case OperandType.Undefined: - return DefaultNames.UndefinedName; - } - - throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."); + OperandType.Argument => GetArgumentName(operand.Value), + OperandType.Attribute => GetAttributeName(operand, config), + OperandType.Constant => NumberFormatter.FormatInt(operand.Value), + OperandType.ConstantBuffer => GetConstantBufferName( + operand.CbufSlot, + operand.CbufOffset, + config.Stage, + config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)), + OperandType.LocalVariable => _locals[operand], + OperandType.Undefined => DefaultNames.UndefinedName, + _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") + }; } public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable) @@ -242,9 +234,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr) { - string suffix = texOp.CbufSlot < 0 ? $"_tcb_{texOp.Handle:X}" : $"_cb{texOp.CbufSlot}_{texOp.Handle:X}"; + return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr); + } - if ((texOp.Type & SamplerType.Indexed) != 0) + public static string GetSamplerName(ShaderStage stage, int cbufSlot, int handle, bool indexed, string indexExpr) + { + string suffix = cbufSlot < 0 ? $"_tcb_{handle:X}" : $"_cb{cbufSlot}_{handle:X}"; + + if (indexed) { suffix += $"a[{indexExpr}]"; } @@ -254,9 +251,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static string GetImageName(ShaderStage stage, AstTextureOperation texOp, string indexExpr) { - string suffix = texOp.CbufSlot < 0 ? $"_tcb_{texOp.Handle:X}_{texOp.Format.ToGlslFormat()}" : $"_cb{texOp.CbufSlot}_{texOp.Handle:X}_{texOp.Format.ToGlslFormat()}"; + return GetImageName(stage, texOp.CbufSlot, texOp.Handle, texOp.Format, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr); + } - if ((texOp.Type & SamplerType.Indexed) != 0) + public static string GetImageName( + ShaderStage stage, + int cbufSlot, + int handle, + TextureFormat format, + bool indexed, + string indexExpr) + { + string suffix = cbufSlot < 0 + ? $"_tcb_{handle:X}_{format.ToGlslFormat()}" + : $"_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}"; + + if (indexed) { suffix += $"a[{indexExpr}]"; } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs index 352d16c07..5a351c73f 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs @@ -82,7 +82,9 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (context.CurrOp) { case IOpCodeCbuf op: - return context.PackDouble2x32(Cbuf(op.Slot, op.Offset), Cbuf(op.Slot, op.Offset + 1)); + return context.PackDouble2x32( + context.Config.CreateCbuf(op.Slot, op.Offset), + context.Config.CreateCbuf(op.Slot, op.Offset + 1)); case IOpCodeImmF op: return context.FP32ConvertToFP64(ConstF(op.Immediate)); @@ -99,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (context.CurrOp) { case IOpCodeCbuf op: - return Cbuf(op.Slot, op.Offset); + return context.Config.CreateCbuf(op.Slot, op.Offset); case IOpCodeImm op: return Const(op.Immediate); @@ -125,7 +127,9 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (context.CurrOp) { case IOpCodeRegCbuf op: - return context.PackDouble2x32(Cbuf(op.Slot, op.Offset), Cbuf(op.Slot, op.Offset + 1)); + return context.PackDouble2x32( + context.Config.CreateCbuf(op.Slot, op.Offset), + context.Config.CreateCbuf(op.Slot, op.Offset + 1)); case IOpCodeRc op: return context.PackDouble2x32(Register(op.Rc.Index, op.Rc.Type), Register(op.Rc.Index | 1, op.Rc.Type)); @@ -136,7 +140,7 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (context.CurrOp) { case IOpCodeRegCbuf op: - return Cbuf(op.Slot, op.Offset); + return context.Config.CreateCbuf(op.Slot, op.Offset); case IOpCodeRc op: return Register(op.Rc); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index a39062946..69d3ffb34 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand rd = Register(rdIndex++, RegisterType.Gpr); - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( Instruction.ImageLoad, type, flags, @@ -132,17 +132,15 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand rd = Register(rdIndex++, RegisterType.Gpr); - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( Instruction.ImageLoad, type, + GetTextureFormat(op.Size), flags, handle, compIndex, rd, - sources) - { - Format = GetTextureFormat(op.Size) - }; + sources); context.Add(operation); @@ -266,17 +264,15 @@ namespace Ryujinx.Graphics.Shader.Instructions TextureFlags flags = op.IsBindless ? TextureFlags.Bindless : TextureFlags.None; - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( Instruction.ImageStore, type, + format, flags, handle, 0, null, - sources) - { - Format = format - }; + sources); context.Add(operation); } @@ -615,7 +611,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { Operand dest = GetDest(); - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( Instruction.TextureSample, type, flags, @@ -764,7 +760,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { Operand dest = GetDest(); - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( Instruction.TextureSample, type, flags, @@ -888,7 +884,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { Operand tempDest = Local(); - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( Instruction.Lod, type, flags, @@ -1027,7 +1023,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { Operand dest = GetDest(); - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( Instruction.TextureSample, type, flags, @@ -1112,7 +1108,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { Operand dest = GetDest(); - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( inst, type, flags, @@ -1277,7 +1273,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { Operand dest = GetDest(); - TextureOperation operation = new TextureOperation( + TextureOperation operation = context.CreateTextureOperation( Instruction.TextureSample, type, flags, diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index 52f02bfb9..b76d5dc6d 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -2,30 +2,30 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { class TextureOperation : Operation { - private const int DefaultCbufSlot = -1; + public const int DefaultCbufSlot = -1; - public SamplerType Type { get; private set; } + public SamplerType Type { get; private set; } + public TextureFormat Format { get; set; } public TextureFlags Flags { get; private set; } public int CbufSlot { get; private set; } - public int Handle { get; private set; } - public TextureFormat Format { get; set; } - public TextureOperation( - Instruction inst, - SamplerType type, - TextureFlags flags, - int handle, - int compIndex, - Operand dest, + Instruction inst, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int handle, + int compIndex, + Operand dest, params Operand[] sources) : base(inst, compIndex, dest, sources) { - Type = type; - Flags = flags; + Type = type; + Format = format; + Flags = flags; CbufSlot = DefaultCbufSlot; - Handle = handle; + Handle = handle; } public void TurnIntoIndexed(int handle) @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation } CbufSlot = cbufSlot; - Handle = handle; + Handle = handle; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index cdb974880..7d1d0ae35 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -4,31 +4,28 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { class AstTextureOperation : AstOperation { - public SamplerType Type { get; } + public SamplerType Type { get; } public TextureFormat Format { get; } - public TextureFlags Flags { get; } + public TextureFlags Flags { get; } - public int CbufSlot { get; } - public int Handle { get; } - public int ArraySize { get; } + public int CbufSlot { get; } + public int Handle { get; } public AstTextureOperation( - Instruction inst, - SamplerType type, - TextureFormat format, - TextureFlags flags, - int cbufSlot, - int handle, - int arraySize, - int index, + Instruction inst, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int cbufSlot, + int handle, + int index, params IAstNode[] sources) : base(inst, index, sources, sources.Length) { - Type = type; - Format = format; - Flags = flags; - CbufSlot = cbufSlot; - Handle = handle; - ArraySize = arraySize; + Type = type; + Format = format; + Flags = flags; + CbufSlot = cbufSlot; + Handle = handle; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 4a0ea8468..25e5edc9a 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -2,7 +2,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; -using System.Numerics; namespace Ryujinx.Graphics.Shader.StructuredIr { @@ -100,7 +99,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr texOp.Flags, texOp.CbufSlot, texOp.Handle, - 4, // TODO: Non-hardcoded array size. texOp.Index, sources); } @@ -109,34 +107,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { AstOperand dest = context.GetOperandDef(operation.Dest); - if (inst == Instruction.LoadConstant) - { - Operand slot = operation.GetSource(0); - - if (slot.Type == OperandType.Constant) - { - context.Info.CBuffers.Add(slot.Value); - } - else - { - // If the value is not constant, then we don't know - // how many constant buffers are used, so we assume - // all of them are used. - int cbCount = 32 - BitOperations.LeadingZeroCount(context.Config.GpuAccessor.QueryConstantBufferUse()); - - for (int index = 0; index < cbCount; index++) - { - context.Info.CBuffers.Add(index); - } - - context.Info.UsesCbIndexing = true; - } - } - else if (UsesStorage(inst)) - { - AddSBufferUse(context.Info.SBuffers, operation); - } - // If all the sources are bool, it's better to use short-circuiting // logical operations, rather than forcing a cast to int and doing // a bitwise operation with the value, as it is likely to be used as @@ -169,23 +139,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (operation is TextureOperation texOp) { - if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore) + if (texOp.Inst == Instruction.ImageLoad) { dest.VarType = texOp.Format.GetComponentType(); } - AstTextureOperation astTexOp = GetAstTextureOperation(texOp); - - if (texOp.Inst == Instruction.ImageLoad) - { - context.Info.Images.Add(astTexOp); - } - else - { - context.Info.Samplers.Add(astTexOp); - } - - source = astTexOp; + source = GetAstTextureOperation(texOp); } else if (!isCopy) { @@ -206,17 +165,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { AstTextureOperation astTexOp = GetAstTextureOperation(texOp); - context.Info.Images.Add(astTexOp); - context.AddNode(astTexOp); } else { - if (UsesStorage(inst)) - { - AddSBufferUse(context.Info.SBuffers, operation); - } - context.AddNode(new AstOperation(inst, operation.Index, sources, operation.SourcesCount)); } @@ -257,26 +209,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } } - private static void AddSBufferUse(HashSet sBuffers, Operation operation) - { - Operand slot = operation.GetSource(0); - - if (slot.Type == OperandType.Constant) - { - sBuffers.Add(slot.Value); - } - else - { - // If the value is not constant, then we don't know - // how many storage buffers are used, so we assume - // all of them are used. - for (int index = 0; index < GlobalMemory.StorageMaxCount; index++) - { - sBuffers.Add(index); - } - } - } - private static VariableType GetVarTypeFromUses(Operand dest) { HashSet visited = new HashSet(); @@ -301,7 +233,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { foreach (INode useNode in operand.UseOps) { - if (!(useNode is Operation operation)) + if (useNode is not Operation operation) { continue; } @@ -340,7 +272,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { foreach (IAstNode node in sources) { - if (!(node is AstOperand operand)) + if (node is not AstOperand operand) { return false; } @@ -356,52 +288,37 @@ namespace Ryujinx.Graphics.Shader.StructuredIr private static bool IsBranchInst(Instruction inst) { - switch (inst) + return inst switch { - case Instruction.Branch: - case Instruction.BranchIfFalse: - case Instruction.BranchIfTrue: - return true; - } - - return false; + Instruction.Branch or + Instruction.BranchIfFalse or + Instruction.BranchIfTrue => true, + _ => false, + }; } private static bool IsBitwiseInst(Instruction inst) { - switch (inst) + return inst switch { - case Instruction.BitwiseAnd: - case Instruction.BitwiseExclusiveOr: - case Instruction.BitwiseNot: - case Instruction.BitwiseOr: - return true; - } - - return false; + Instruction.BitwiseAnd or + Instruction.BitwiseExclusiveOr or + Instruction.BitwiseNot or + Instruction.BitwiseOr => true, + _ => false + }; } private static Instruction GetLogicalFromBitwiseInst(Instruction inst) { - switch (inst) + return inst switch { - case Instruction.BitwiseAnd: return Instruction.LogicalAnd; - case Instruction.BitwiseExclusiveOr: return Instruction.LogicalExclusiveOr; - case Instruction.BitwiseNot: return Instruction.LogicalNot; - case Instruction.BitwiseOr: return Instruction.LogicalOr; - } - - throw new ArgumentException($"Unexpected instruction \"{inst}\"."); - } - - private static bool UsesStorage(Instruction inst) - { - if (inst == Instruction.LoadStorage || inst == Instruction.StoreStorage) - { - return true; - } - - return inst.IsAtomic() && (inst & Instruction.MrMask) == Instruction.MrStorage; + Instruction.BitwiseAnd => Instruction.LogicalAnd, + Instruction.BitwiseExclusiveOr => Instruction.LogicalExclusiveOr, + Instruction.BitwiseNot => Instruction.LogicalNot, + Instruction.BitwiseOr => Instruction.LogicalOr, + _ => throw new ArgumentException($"Unexpected instruction \"{inst}\".") + }; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index c4d8b85fb..73fff1ec1 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -291,10 +291,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { Info.IAttributes.Add(attrIndex); } - else if (operand.Type == OperandType.ConstantBuffer) - { - Info.CBuffers.Add(operand.GetCbufSlot()); - } return GetOperand(operand); } diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index d1619bfa4..9479d5357 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -6,31 +6,17 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { public List Functions { get; } - public HashSet CBuffers { get; } - public HashSet SBuffers { get; } - public HashSet IAttributes { get; } public HashSet OAttributes { get; } - public bool UsesCbIndexing { get; set; } - public HelperFunctionsMask HelperFunctionsMask { get; set; } - public HashSet Samplers { get; } - public HashSet Images { get; } - public StructuredProgramInfo() { Functions = new List(); - CBuffers = new HashSet(); - SBuffers = new HashSet(); - IAttributes = new HashSet(); OAttributes = new HashSet(); - - Samplers = new HashSet(); - Images = new HashSet(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 9b2201773..49a893748 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -53,6 +53,36 @@ namespace Ryujinx.Graphics.Shader.Translation _operations.Add(operation); } + public TextureOperation CreateTextureOperation( + Instruction inst, + SamplerType type, + TextureFlags flags, + int handle, + int compIndex, + Operand dest, + params Operand[] sources) + { + return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dest, sources); + } + + public TextureOperation CreateTextureOperation( + Instruction inst, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int handle, + int compIndex, + Operand dest, + params Operand[] sources) + { + if (!flags.HasFlag(TextureFlags.Bindless)) + { + Config.SetUsedTexture(inst, type, format, flags, TextureOperation.DefaultCbufSlot, handle); + } + + return new TextureOperation(inst, type, format, flags, handle, compIndex, dest, sources); + } + public void FlagAttributeRead(int attribute) { if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId) diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index dcefb5913..e5ba04bc3 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -518,6 +518,15 @@ namespace Ryujinx.Graphics.Shader.Translation public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b) { + if (a.Type == OperandType.Constant) + { + context.Config.SetUsedConstantBuffer(a.Value); + } + else + { + context.Config.SetUsedFeature(FeatureFlags.CbIndexing); + } + return context.Add(Instruction.LoadConstant, Local(), a, b); } diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index 1b7128961..b0c48410b 100644 --- a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation FragCoordXY = 1 << 1, Bindless = 1 << 2, - - InstanceId = 1 << 3 + InstanceId = 1 << 3, + CbIndexing = 1 << 4 } } diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index f91a00eba..3a523adc2 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (bindlessHandle.Type == OperandType.ConstantBuffer) { - texOp.SetHandle(bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot()); + SetHandle(config, texOp, bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot()); continue; } @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - texOp.SetHandle(src0.GetCbufOffset() | (src1.GetCbufOffset() << 16), src0.GetCbufSlot()); + SetHandle(config, texOp, src0.GetCbufOffset() | (src1.GetCbufOffset() << 16), src0.GetCbufSlot()); } else if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore) { @@ -64,11 +64,19 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (src0.Type == OperandType.ConstantBuffer) { - texOp.SetHandle(src0.GetCbufOffset(), src0.GetCbufSlot()); - texOp.Format = config.GetTextureFormat(texOp.Handle, texOp.CbufSlot); + int cbufOffset = src0.GetCbufOffset(); + int cbufSlot = src0.GetCbufSlot(); + texOp.Format = config.GetTextureFormat(cbufOffset, cbufSlot); + SetHandle(config, texOp, cbufOffset, cbufSlot); } } } } + + private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot) + { + texOp.SetHandle(cbufOffset, cbufSlot); + config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, cbufSlot, cbufOffset); + } } } diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs index 41f42dad1..ca46a1f53 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class BindlessToIndexed { - public static void RunPass(BasicBlock block) + public static void RunPass(BasicBlock block, ShaderConfig config) { // We can turn a bindless texture access into a indexed access, // as long the following conditions are true: @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - texOp.TurnIntoIndexed(addSrc1.Value / 4); + TurnIntoIndexed(config, texOp, addSrc1.Value / 4); Operand index = Local(); @@ -75,5 +75,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations texOp.SetSource(0, index); } } + + private static void TurnIntoIndexed(ShaderConfig config, TextureOperation texOp, int handle) + { + texOp.TurnIntoIndexed(handle); + config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, handle); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index a34175449..bccb0cbe3 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -58,11 +58,16 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { Operation operation = (Operation)node.Value; + bool isAtomic = operation.Inst.IsAtomic(); + bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal; + + config.SetUsedStorageBuffer(storageIndex, isWrite); + Operand GetStorageOffset() { Operand addrLow = operation.GetSource(0); - Operand baseAddrLow = Cbuf(0, GetStorageCbOffset(config.Stage, storageIndex)); + Operand baseAddrLow = config.CreateCbuf(0, GetStorageCbOffset(config.Stage, storageIndex)); Operand baseAddrTrunc = Local(); @@ -96,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operation storageOp; - if (operation.Inst.IsAtomic()) + if (isAtomic) { Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage; @@ -133,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { Operand addrLow = operation.GetSource(0); - Operand baseAddrLow = Cbuf(0, UbeBaseOffset + storageIndex * StorageDescSize); + Operand baseAddrLow = config.CreateCbuf(0, UbeBaseOffset + storageIndex * StorageDescSize); Operand baseAddrTrunc = Local(); @@ -157,9 +162,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand[] sources = new Operand[operation.SourcesCount]; - sources[0] = Const(UbeFirstCbuf + storageIndex); + int cbSlot = UbeFirstCbuf + storageIndex; + + sources[0] = Const(cbSlot); sources[1] = GetCbufOffset(); + config.SetUsedConstantBuffer(cbSlot); + for (int index = 2; index < operation.SourcesCount; index++) { sources[index] = operation.GetSource(index); diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index ec8d80159..d184e6b4b 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { GlobalToStorage.RunPass(blocks[blkIndex], config); - BindlessToIndexed.RunPass(blocks[blkIndex]); + BindlessToIndexed.RunPass(blocks[blkIndex], config); BindlessElimination.RunPass(blocks[blkIndex], config); } diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 5427c013a..07eeae482 100644 --- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -48,6 +48,9 @@ namespace Ryujinx.Graphics.Shader.Translation { Operation operation = (Operation)node.Value; + bool isAtomic = operation.Inst.IsAtomic(); + bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal; + Operation storageOp; Operand PrependOperation(Instruction inst, params Operand[] sources) @@ -67,11 +70,13 @@ namespace Ryujinx.Graphics.Shader.Translation for (int slot = 0; slot < StorageMaxCount; slot++) { + config.SetUsedStorageBuffer(slot, isWrite); + int cbOffset = GetStorageCbOffset(config.Stage, slot); - Operand baseAddrLow = Cbuf(0, cbOffset); - Operand baseAddrHigh = Cbuf(0, cbOffset + 1); - Operand size = Cbuf(0, cbOffset + 2); + Operand baseAddrLow = config.CreateCbuf(0, cbOffset); + Operand baseAddrHigh = config.CreateCbuf(0, cbOffset + 1); + Operand size = config.CreateCbuf(0, cbOffset + 2); Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow); Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow); @@ -104,7 +109,7 @@ namespace Ryujinx.Graphics.Shader.Translation sources[index] = operation.GetSource(index); } - if (operation.Inst.IsAtomic()) + if (isAtomic) { Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage; @@ -303,6 +308,7 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new TextureOperation( Instruction.TextureSize, texOp.Type, + texOp.Format, texOp.Flags, texOp.Handle, index, @@ -350,6 +356,7 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new TextureOperation( Instruction.Lod, texOp.Type, + texOp.Format, texOp.Flags, texOp.Handle, 1, @@ -374,6 +381,7 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new TextureOperation( Instruction.TextureSize, texOp.Type, + texOp.Format, texOp.Flags, texOp.Handle, index, @@ -409,6 +417,7 @@ namespace Ryujinx.Graphics.Shader.Translation TextureOperation newTexOp = new TextureOperation( Instruction.TextureSample, texOp.Type, + texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), texOp.Handle, componentIndex, diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 077ce70d9..3230f4e68 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -1,9 +1,16 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; using System.Collections.Generic; +using System.Linq; +using System.Numerics; namespace Ryujinx.Graphics.Shader.Translation { class ShaderConfig { + // TODO: Non-hardcoded array size. + public const int SamplerArraySize = 4; + public ShaderStage Stage { get; } public bool GpPassthrough { get; } @@ -24,8 +31,6 @@ namespace Ryujinx.Graphics.Shader.Translation public TranslationFlags Flags { get; } - public TranslationCounts Counts { get; } - public int Size { get; private set; } public byte ClipDistancesWritten { get; private set; } @@ -34,42 +39,80 @@ namespace Ryujinx.Graphics.Shader.Translation public HashSet TextureHandlesForCache { get; } + private readonly TranslationCounts _counts; + + private int _usedConstantBuffers; + private int _usedStorageBuffers; + private int _usedStorageBuffersWrite; + + private struct TextureInfo : IEquatable + { + public int CbufSlot { get; } + public int Handle { get; } + public bool Indexed { get; } + public TextureFormat Format { get; } + + public TextureInfo(int cbufSlot, int handle, bool indexed, TextureFormat format) + { + CbufSlot = cbufSlot; + Handle = handle; + Indexed = indexed; + Format = format; + } + + public override bool Equals(object obj) + { + return obj is TextureInfo other && Equals(other); + } + + public bool Equals(TextureInfo other) + { + return CbufSlot == other.CbufSlot && Handle == other.Handle && Indexed == other.Indexed && Format == other.Format; + } + + public override int GetHashCode() + { + return HashCode.Combine(CbufSlot, Handle, Indexed, Format); + } + } + + private struct TextureMeta + { + public bool AccurateType; + public SamplerType Type; + public TextureUsageFlags UsageFlags; + } + + private readonly Dictionary _usedTextures; + private readonly Dictionary _usedImages; + + private BufferDescriptor[] _cachedConstantBufferDescriptors; + private BufferDescriptor[] _cachedStorageBufferDescriptors; + private TextureDescriptor[] _cachedTextureDescriptors; + private TextureDescriptor[] _cachedImageDescriptors; + public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) { Stage = ShaderStage.Compute; - GpPassthrough = false; - OutputTopology = OutputTopology.PointList; - MaxOutputVertices = 0; - LocalMemorySize = 0; - ImapTypes = null; - OmapTargets = null; - OmapSampleMask = false; - OmapDepth = false; GpuAccessor = gpuAccessor; Flags = flags; - Size = 0; - UsedFeatures = FeatureFlags.None; - Counts = counts; + _counts = counts; TextureHandlesForCache = new HashSet(); + _usedTextures = new Dictionary(); + _usedImages = new Dictionary(); } - public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) + public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) : this(gpuAccessor, flags, counts) { - Stage = header.Stage; - GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; - OutputTopology = header.OutputTopology; - MaxOutputVertices = header.MaxOutputVertexCount; - LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize; - ImapTypes = header.ImapTypes; - OmapTargets = header.OmapTargets; - OmapSampleMask = header.OmapSampleMask; - OmapDepth = header.OmapDepth; - GpuAccessor = gpuAccessor; - Flags = flags; - Size = 0; - UsedFeatures = FeatureFlags.None; - Counts = counts; - TextureHandlesForCache = new HashSet(); + Stage = header.Stage; + GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; + OutputTopology = header.OutputTopology; + MaxOutputVertices = header.MaxOutputVertexCount; + LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize; + ImapTypes = header.ImapTypes; + OmapTargets = header.OmapTargets; + OmapSampleMask = header.OmapSampleMask; + OmapDepth = header.OmapDepth; } public int GetDepthRegister() @@ -126,5 +169,199 @@ namespace Ryujinx.Graphics.Shader.Translation { UsedFeatures |= flags; } + + public Operand CreateCbuf(int slot, int offset) + { + SetUsedConstantBuffer(slot); + return OperandHelper.Cbuf(slot, offset); + } + + public void SetUsedConstantBuffer(int slot) + { + _usedConstantBuffers |= 1 << slot; + } + + public void SetUsedStorageBuffer(int slot, bool write) + { + int mask = 1 << slot; + _usedStorageBuffers |= mask; + + if (write) + { + _usedStorageBuffersWrite |= mask; + } + } + + public void SetUsedTexture( + Instruction inst, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int cbufSlot, + int handle) + { + inst &= Instruction.Mask; + bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore; + bool isWrite = inst == Instruction.ImageStore; + bool accurateType = inst != Instruction.TextureSize && inst != Instruction.Lod; + + if (isImage) + { + SetUsedTextureOrImage(_usedImages, cbufSlot, handle, type, format, true, isWrite, false); + } + else + { + SetUsedTextureOrImage(_usedTextures, cbufSlot, handle, type, TextureFormat.Unknown, flags.HasFlag(TextureFlags.IntCoords), false, accurateType); + } + } + + private static void SetUsedTextureOrImage( + Dictionary dict, + int cbufSlot, + int handle, + SamplerType type, + TextureFormat format, + bool intCoords, + bool write, + bool accurateType) + { + var dimensions = type.GetDimensions(); + var isArray = type.HasFlag(SamplerType.Array); + var isIndexed = type.HasFlag(SamplerType.Indexed); + + var usageFlags = TextureUsageFlags.None; + + if (intCoords) + { + usageFlags |= TextureUsageFlags.NeedsScaleValue; + + var canScale = (dimensions == 2 && !isArray) || (dimensions == 3 && isArray); + if (!canScale) + { + // Resolution scaling cannot be applied to this texture right now. + // Flag so that we know to blacklist scaling on related textures when binding them. + usageFlags |= TextureUsageFlags.ResScaleUnsupported; + } + } + + if (write) + { + usageFlags |= TextureUsageFlags.ImageStore; + } + + int arraySize = isIndexed ? SamplerArraySize : 1; + + for (int layer = 0; layer < arraySize; layer++) + { + var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format); + var meta = new TextureMeta() + { + AccurateType = accurateType, + Type = type, + UsageFlags = usageFlags + }; + + if (dict.TryGetValue(info, out var existingMeta)) + { + meta.UsageFlags |= existingMeta.UsageFlags; + + // If the texture we have has inaccurate type information, then + // we prefer the most accurate one. + if (existingMeta.AccurateType) + { + meta.AccurateType = true; + meta.Type = existingMeta.Type; + } + + dict[info] = meta; + } + else + { + dict.Add(info, meta); + } + } + } + + public BufferDescriptor[] GetConstantBufferDescriptors() + { + if (_cachedConstantBufferDescriptors != null) + { + return _cachedConstantBufferDescriptors; + } + + int usedMask = _usedConstantBuffers; + + if (UsedFeatures.HasFlag(FeatureFlags.CbIndexing)) + { + usedMask = FillMask(usedMask); + } + + return _cachedConstantBufferDescriptors = GetBufferDescriptors(usedMask, 0, _counts.IncrementUniformBuffersCount); + } + + public BufferDescriptor[] GetStorageBufferDescriptors() + { + return _cachedStorageBufferDescriptors ??= GetBufferDescriptors(FillMask(_usedStorageBuffers), _usedStorageBuffersWrite, _counts.IncrementStorageBuffersCount); + } + + private static int FillMask(int mask) + { + // When the storage or uniform buffers are used as array, we must allocate a binding + // even for the "gaps" that are not used on the shader. + // For this reason, fill up the gaps so that all slots up to the highest one are + // marked as "used". + return mask != 0 ? (int)(uint.MaxValue >> BitOperations.LeadingZeroCount((uint)mask)) : 0; + } + + private static BufferDescriptor[] GetBufferDescriptors(int usedMask, int writtenMask, Func getBindingCallback) + { + var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)]; + + for (int i = 0; i < descriptors.Length; i++) + { + int slot = BitOperations.TrailingZeroCount(usedMask); + + descriptors[i] = new BufferDescriptor(getBindingCallback(), slot); + + if ((writtenMask & (1 << slot)) != 0) + { + descriptors[i].SetFlag(BufferUsageFlags.Write); + } + + usedMask &= ~(1 << slot); + } + + return descriptors; + } + + public TextureDescriptor[] GetTextureDescriptors() + { + return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, _counts.IncrementTexturesCount); + } + + public TextureDescriptor[] GetImageDescriptors() + { + return _cachedImageDescriptors ??= GetTextureOrImageDescriptors(_usedImages, _counts.IncrementImagesCount); + } + + private static TextureDescriptor[] GetTextureOrImageDescriptors(Dictionary dict, Func getBindingCallback) + { + var descriptors = new TextureDescriptor[dict.Count]; + + int i = 0; + foreach (var kv in dict.OrderBy(x => x.Key.Indexed).OrderBy(x => x.Key.Handle)) + { + var info = kv.Key; + var meta = kv.Value; + + int binding = getBindingCallback(); + + descriptors[i] = new TextureDescriptor(binding, meta.Type, info.Format, info.CbufSlot, info.Handle); + descriptors[i].SetFlag(meta.UsageFlags); + i++; + } + + return descriptors; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 1c15ccf27..74c6a653b 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -87,18 +87,16 @@ namespace Ryujinx.Graphics.Shader.Translation StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(funcs, config); - GlslProgram program = GlslGenerator.Generate(sInfo, config); + string glslCode = GlslGenerator.Generate(sInfo, config); shaderProgramInfo = new ShaderProgramInfo( - program.CBufferDescriptors, - program.SBufferDescriptors, - program.TextureDescriptors, - program.ImageDescriptors, + config.GetConstantBufferDescriptors(), + config.GetStorageBufferDescriptors(), + config.GetTextureDescriptors(), + config.GetImageDescriptors(), config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), config.ClipDistancesWritten); - string glslCode = program.Code; - return new ShaderProgram(config.Stage, glslCode); } @@ -112,7 +110,7 @@ namespace Ryujinx.Graphics.Shader.Translation Block[][] cfg; ulong maxEndAddress = 0; - bool hasBindless = false; + bool hasBindless; if ((flags & TranslationFlags.Compute) != 0) {