mirror of
				https://github.com/Ryujinx/Ryujinx.git
				synced 2025-11-04 01:03:43 +00:00 
			
		
		
		
	Replace constant buffer access on shader with new Load instruction (#4646)
This commit is contained in:
		
							parent
							
								
									fb27042e01
								
							
						
					
					
						commit
						402f05b8ef
					
				@ -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";
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Creates a new GPU accessor.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.StructuredIr;
 | 
			
		||||
using Ryujinx.Graphics.Shader.Translation;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
 | 
			
		||||
@ -102,13 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 | 
			
		||||
                context.AppendLine();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var cBufferDescriptors = context.Config.GetConstantBufferDescriptors();
 | 
			
		||||
            if (cBufferDescriptors.Length != 0)
 | 
			
		||||
            {
 | 
			
		||||
                DeclareUniforms(context, cBufferDescriptors);
 | 
			
		||||
 | 
			
		||||
                context.AppendLine();
 | 
			
		||||
            }
 | 
			
		||||
            DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
 | 
			
		||||
 | 
			
		||||
            var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
 | 
			
		||||
            if (sBufferDescriptors.Length != 0)
 | 
			
		||||
@ -265,18 +260,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 | 
			
		||||
                        scaleElements++; // Also includes render target scale, for gl_FragCoord.
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    DeclareSupportUniformBlock(context, context.Config.Stage, scaleElements);
 | 
			
		||||
 | 
			
		||||
                    if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
 | 
			
		||||
                        context.AppendLine();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (isFragment || context.Config.Stage == ShaderStage.Vertex)
 | 
			
		||||
                {
 | 
			
		||||
                    DeclareSupportUniformBlock(context, context.Config.Stage, 0);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
 | 
			
		||||
@ -389,36 +378,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void DeclareUniforms(CodeGenContext context, BufferDescriptor[] descriptors)
 | 
			
		||||
        private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
 | 
			
		||||
        {
 | 
			
		||||
            string ubSize = "[" + NumberFormatter.FormatInt(Constants.ConstantBufferSize / 16) + "]";
 | 
			
		||||
 | 
			
		||||
            if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
 | 
			
		||||
            foreach (BufferDefinition buffer in buffers)
 | 
			
		||||
            {
 | 
			
		||||
                string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
 | 
			
		||||
 | 
			
		||||
                ubName += "_" + DefaultNames.UniformNamePrefix;
 | 
			
		||||
 | 
			
		||||
                string blockName = $"{ubName}_{DefaultNames.BlockSuffix}";
 | 
			
		||||
 | 
			
		||||
                context.AppendLine($"layout (binding = {context.Config.FirstConstantBufferBinding}, std140) uniform {blockName}");
 | 
			
		||||
                context.EnterScope();
 | 
			
		||||
                context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";");
 | 
			
		||||
                context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];");
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var descriptor in descriptors)
 | 
			
		||||
                string layout = buffer.Layout switch
 | 
			
		||||
                {
 | 
			
		||||
                    string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
 | 
			
		||||
                    BufferLayout.Std140 => "std140",
 | 
			
		||||
                    _ => "std430"
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                    ubName += "_" + DefaultNames.UniformNamePrefix + descriptor.Slot;
 | 
			
		||||
                context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}");
 | 
			
		||||
                context.EnterScope();
 | 
			
		||||
 | 
			
		||||
                    context.AppendLine($"layout (binding = {descriptor.Binding}, std140) uniform {ubName}");
 | 
			
		||||
                    context.EnterScope();
 | 
			
		||||
                    context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, descriptor.Slot, false) + ubSize + ";");
 | 
			
		||||
                    context.LeaveScope(";");
 | 
			
		||||
                foreach (StructureField field in buffer.Type.Fields)
 | 
			
		||||
                {
 | 
			
		||||
                    if (field.Type.HasFlag(AggregateType.Array))
 | 
			
		||||
                    {
 | 
			
		||||
                        string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array);
 | 
			
		||||
                        string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
 | 
			
		||||
 | 
			
		||||
                        context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        string typeName = GetVarTypeName(context, field.Type);
 | 
			
		||||
 | 
			
		||||
                        context.AppendLine($"{typeName} {field.Name};");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                context.LeaveScope($" {buffer.Name};");
 | 
			
		||||
                context.AppendLine();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -759,39 +750,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 | 
			
		||||
            context.AppendLine($"layout (location = {location}) patch out vec4 {name};");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
 | 
			
		||||
        {
 | 
			
		||||
            bool needsSupportBlock = stage == ShaderStage.Fragment ||
 | 
			
		||||
                (context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable());
 | 
			
		||||
 | 
			
		||||
            if (!needsSupportBlock && scaleElements == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}");
 | 
			
		||||
            context.EnterScope();
 | 
			
		||||
 | 
			
		||||
            switch (stage)
 | 
			
		||||
            {
 | 
			
		||||
                case ShaderStage.Fragment:
 | 
			
		||||
                case ShaderStage.Vertex:
 | 
			
		||||
                    context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
 | 
			
		||||
                    context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
 | 
			
		||||
                    context.AppendLine($"vec4 {DefaultNames.SupportBlockViewportInverse};");
 | 
			
		||||
                    context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ShaderStage.Compute:
 | 
			
		||||
                    context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];");
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{SupportBuffer.RenderScaleMaxCount}];");
 | 
			
		||||
 | 
			
		||||
            context.LeaveScope(";");
 | 
			
		||||
            context.AppendLine();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void AppendHelperFunction(CodeGenContext context, string filename)
 | 
			
		||||
        {
 | 
			
		||||
            string code = EmbeddedResources.ReadAllText(filename);
 | 
			
		||||
 | 
			
		||||
@ -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";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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),
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
@ -23,9 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 | 
			
		||||
 | 
			
		||||
        public int InputVertices { get; }
 | 
			
		||||
 | 
			
		||||
        public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>();
 | 
			
		||||
        public Instruction SupportBuffer { get; set; }
 | 
			
		||||
        public Instruction UniformBuffersArray { get; set; }
 | 
			
		||||
        public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
 | 
			
		||||
        public Instruction StorageBuffersArray { get; set; }
 | 
			
		||||
        public Instruction LocalMemory { get; set; }
 | 
			
		||||
        public Instruction SharedMemory { get; set; }
 | 
			
		||||
@ -217,7 +215,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 | 
			
		||||
                {
 | 
			
		||||
                    IrOperandType.Argument => GetArgument(type, operand),
 | 
			
		||||
                    IrOperandType.Constant => GetConstant(type, operand),
 | 
			
		||||
                    IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
 | 
			
		||||
                    IrOperandType.LocalVariable => GetLocal(type, operand),
 | 
			
		||||
                    IrOperandType.Undefined => GetUndefined(type),
 | 
			
		||||
                    _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
 | 
			
		||||
@ -274,31 +271,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Instruction GetConstantBuffer(AggregateType type, AstOperand operand)
 | 
			
		||||
        {
 | 
			
		||||
            var i1 = Constant(TypeS32(), 0);
 | 
			
		||||
            var i2 = Constant(TypeS32(), operand.CbufOffset >> 2);
 | 
			
		||||
            var i3 = Constant(TypeU32(), operand.CbufOffset & 3);
 | 
			
		||||
 | 
			
		||||
            Instruction elemPointer;
 | 
			
		||||
 | 
			
		||||
            if (UniformBuffersArray != null)
 | 
			
		||||
            {
 | 
			
		||||
                var ubVariable = UniformBuffersArray;
 | 
			
		||||
                var i0 = Constant(TypeS32(), operand.CbufSlot);
 | 
			
		||||
 | 
			
		||||
                elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i0, i1, i2, i3);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var ubVariable = UniformBuffers[operand.CbufSlot];
 | 
			
		||||
 | 
			
		||||
                elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i1, i2, i3);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return BitcastIfNeeded(type, AggregateType.FP32, Load(TypeFP32(), elemPointer));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Instruction GetLocalPointer(AstOperand local)
 | 
			
		||||
        {
 | 
			
		||||
            return _locals[local];
 | 
			
		||||
 | 
			
		||||
@ -98,8 +98,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 | 
			
		||||
                DeclareLocalMemory(context, localMemorySize);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            DeclareSupportBuffer(context);
 | 
			
		||||
            DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
 | 
			
		||||
            DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
 | 
			
		||||
            DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
 | 
			
		||||
            DeclareSamplers(context, context.Config.GetTextureDescriptors());
 | 
			
		||||
            DeclareImages(context, context.Config.GetImageDescriptors());
 | 
			
		||||
@ -127,84 +126,63 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 | 
			
		||||
            return variable;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void DeclareSupportBuffer(CodeGenContext context)
 | 
			
		||||
        private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
 | 
			
		||||
        {
 | 
			
		||||
            if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
 | 
			
		||||
            HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>();
 | 
			
		||||
 | 
			
		||||
            foreach (BufferDefinition buffer in buffers)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
                int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4;
 | 
			
		||||
                int alignmentMask = alignment - 1;
 | 
			
		||||
                int offset = 0;
 | 
			
		||||
 | 
			
		||||
            var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
 | 
			
		||||
            var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
 | 
			
		||||
            var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
 | 
			
		||||
                SpvInstruction[] structFieldTypes = new SpvInstruction[buffer.Type.Fields.Length];
 | 
			
		||||
                int[] structFieldOffsets = new int[buffer.Type.Fields.Length];
 | 
			
		||||
 | 
			
		||||
            context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
 | 
			
		||||
            context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
 | 
			
		||||
                for (int fieldIndex = 0; fieldIndex < buffer.Type.Fields.Length; fieldIndex++)
 | 
			
		||||
                {
 | 
			
		||||
                    StructureField field = buffer.Type.Fields[fieldIndex];
 | 
			
		||||
                    int fieldSize = (field.Type.GetSizeInBytes() + alignmentMask) & ~alignmentMask;
 | 
			
		||||
 | 
			
		||||
            var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, viewportInverseVectorType, context.TypeS32(), renderScaleArrayType);
 | 
			
		||||
                    structFieldTypes[fieldIndex] = context.GetType(field.Type, field.ArrayLength);
 | 
			
		||||
                    structFieldOffsets[fieldIndex] = offset;
 | 
			
		||||
 | 
			
		||||
            context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset);
 | 
			
		||||
            context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
 | 
			
		||||
            context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset);
 | 
			
		||||
            context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
 | 
			
		||||
            context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
 | 
			
		||||
            context.Decorate(supportBufferStructType, Decoration.Block);
 | 
			
		||||
                    if (field.Type.HasFlag(AggregateType.Array))
 | 
			
		||||
                    {
 | 
			
		||||
                        // We can't decorate the type more than once.
 | 
			
		||||
                        if (decoratedTypes.Add(structFieldTypes[fieldIndex]))
 | 
			
		||||
                        {
 | 
			
		||||
                            context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
            var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
 | 
			
		||||
            var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform);
 | 
			
		||||
                        offset += fieldSize * field.ArrayLength;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        offset += fieldSize;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0);
 | 
			
		||||
            context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0);
 | 
			
		||||
                var ubStructType = context.TypeStruct(false, structFieldTypes);
 | 
			
		||||
 | 
			
		||||
            context.AddGlobalVariable(supportBufferVariable);
 | 
			
		||||
                if (decoratedTypes.Add(ubStructType))
 | 
			
		||||
                {
 | 
			
		||||
                    context.Decorate(ubStructType, Decoration.Block);
 | 
			
		||||
 | 
			
		||||
            context.SupportBuffer = supportBufferVariable;
 | 
			
		||||
        }
 | 
			
		||||
                    for (int fieldIndex = 0; fieldIndex < structFieldOffsets.Length; fieldIndex++)
 | 
			
		||||
                    {
 | 
			
		||||
                        context.MemberDecorate(ubStructType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
 | 
			
		||||
        {
 | 
			
		||||
            if (descriptors.Length == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uint ubSize = Constants.ConstantBufferSize / 16;
 | 
			
		||||
 | 
			
		||||
            var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true);
 | 
			
		||||
            context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16);
 | 
			
		||||
            var ubStructType = context.TypeStruct(true, ubArrayType);
 | 
			
		||||
            context.Decorate(ubStructType, Decoration.Block);
 | 
			
		||||
            context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0);
 | 
			
		||||
 | 
			
		||||
            if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
 | 
			
		||||
            {
 | 
			
		||||
                int count = descriptors.Max(x => x.Slot) + 1;
 | 
			
		||||
 | 
			
		||||
                var ubStructArrayType = context.TypeArray(ubStructType, context.Constant(context.TypeU32(), count));
 | 
			
		||||
                var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructArrayType);
 | 
			
		||||
                var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
 | 
			
		||||
                var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
 | 
			
		||||
 | 
			
		||||
                context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u");
 | 
			
		||||
                context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
 | 
			
		||||
                context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding);
 | 
			
		||||
                context.Name(ubVariable, buffer.Name);
 | 
			
		||||
                context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
 | 
			
		||||
                context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding);
 | 
			
		||||
                context.AddGlobalVariable(ubVariable);
 | 
			
		||||
 | 
			
		||||
                context.UniformBuffersArray = ubVariable;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
 | 
			
		||||
 | 
			
		||||
                foreach (var descriptor in descriptors)
 | 
			
		||||
                {
 | 
			
		||||
                    var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
 | 
			
		||||
 | 
			
		||||
                    context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}");
 | 
			
		||||
                    context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
 | 
			
		||||
                    context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
 | 
			
		||||
                    context.AddGlobalVariable(ubVariable);
 | 
			
		||||
                    context.UniformBuffers.Add(descriptor.Slot, ubVariable);
 | 
			
		||||
                }
 | 
			
		||||
                context.ConstantBuffers.Add(buffer.Binding, ubVariable);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -394,25 +372,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var ioDefinition in info.IoDefinitions)
 | 
			
		||||
            {
 | 
			
		||||
                var ioVariable = ioDefinition.IoVariable;
 | 
			
		||||
 | 
			
		||||
                // Those are actually from constant buffer, rather than being actual inputs or outputs,
 | 
			
		||||
                // so we must ignore them here as they are declared as part of the support buffer.
 | 
			
		||||
                // TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input").
 | 
			
		||||
                if (ioVariable == IoVariable.FragmentOutputIsBgra ||
 | 
			
		||||
                    ioVariable == IoVariable.SupportBlockRenderScale ||
 | 
			
		||||
                    ioVariable == IoVariable.SupportBlockViewInverse)
 | 
			
		||||
                {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                bool isOutput = ioDefinition.StorageKind.IsOutput();
 | 
			
		||||
                bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
 | 
			
		||||
 | 
			
		||||
                PixelImap iq = PixelImap.Unused;
 | 
			
		||||
 | 
			
		||||
                if (context.Config.Stage == ShaderStage.Fragment)
 | 
			
		||||
                {
 | 
			
		||||
                    var ioVariable = ioDefinition.IoVariable;
 | 
			
		||||
                    if (ioVariable == IoVariable.UserDefined)
 | 
			
		||||
                    {
 | 
			
		||||
                        iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType();
 | 
			
		||||
@ -429,6 +393,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                bool isOutput = ioDefinition.StorageKind.IsOutput();
 | 
			
		||||
                bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
 | 
			
		||||
 | 
			
		||||
                DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -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)));
 | 
			
		||||
 | 
			
		||||
@ -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));
 | 
			
		||||
 | 
			
		||||
@ -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())
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
 | 
			
		||||
        ImageAtomic,
 | 
			
		||||
        IsNan,
 | 
			
		||||
        Load,
 | 
			
		||||
        LoadConstant,
 | 
			
		||||
        LoadGlobal,
 | 
			
		||||
        LoadLocal,
 | 
			
		||||
        LoadShared,
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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<IAstNode>();
 | 
			
		||||
@ -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()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/Ryujinx.Graphics.Shader/StructuredIr/BufferDefinition.cs
									
									
									
									
									
										Normal file
									
								
							@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/Ryujinx.Graphics.Shader/StructuredIr/BufferLayout.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
namespace Ryujinx.Graphics.Shader.StructuredIr
 | 
			
		||||
{
 | 
			
		||||
    enum BufferLayout
 | 
			
		||||
    {
 | 
			
		||||
        Std140,
 | 
			
		||||
        Std430
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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}\".")
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/Ryujinx.Graphics.Shader/StructuredIr/ShaderProperties.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Shader.StructuredIr
 | 
			
		||||
{
 | 
			
		||||
    class ShaderProperties
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Dictionary<int, BufferDefinition> _constantBuffers;
 | 
			
		||||
 | 
			
		||||
        public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
 | 
			
		||||
 | 
			
		||||
        public ShaderProperties()
 | 
			
		||||
        {
 | 
			
		||||
            _constantBuffers = new Dictionary<int, BufferDefinition>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AddConstantBuffer(int binding, BufferDefinition definition)
 | 
			
		||||
        {
 | 
			
		||||
            _constantBuffers[binding] = definition;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Ryujinx.Graphics.Shader/StructuredIr/StructureType.cs
									
									
									
									
									
										Normal file
									
								
							@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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++)
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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<int> FragmentAlphaTest;
 | 
			
		||||
        public Array8<Vector4<int>> FragmentIsBgra;
 | 
			
		||||
        public Vector4<float> ViewportInverse;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
@ -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++)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -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))
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										126
									
								
								src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
									
									
									
									
									
										Normal file
									
								
							@ -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<int> _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<int>();
 | 
			
		||||
 | 
			
		||||
            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];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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<INode> nextNode = node.Next;
 | 
			
		||||
 | 
			
		||||
                    if (operation is TextureOperation texOp)
 | 
			
		||||
@ -71,6 +76,84 @@ namespace Ryujinx.Graphics.Shader.Translation
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void InsertVectorComponentSelect(LinkedListNode<INode> 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<INode> RewriteGlobalAccess(LinkedListNode<INode> 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);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -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<int, int> _sbSlots;
 | 
			
		||||
        private readonly Dictionary<int, int> _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<int, int>();
 | 
			
		||||
            _sbSlotsReverse = new Dictionary<int, int>();
 | 
			
		||||
 | 
			
		||||
            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<int, int> 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(),
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user