From 03711dd7b5d44e20fb45c728803ea6b9599dec87 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 21 Apr 2020 20:35:28 -0300 Subject: [PATCH] Implement SULD shader instruction (#1117) * Implement SULD shader instruction * Some nits --- Ryujinx.Graphics.Gpu/Engine/Compute.cs | 39 +-- Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 11 + Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 153 ++++++++-- .../CodeGen/Glsl/Declarations.cs | 19 +- .../CodeGen/Glsl/Instructions/InstGen.cs | 36 +-- .../Glsl/Instructions/InstGenMemory.cs | 54 ++-- .../CodeGen/Glsl/OperandManager.cs | 6 + .../Decoders/OpCodeTable.cs | 1 + .../Instructions/InstEmitTexture.cs | 262 ++++++++++++++++-- .../TextureOperation.cs | 2 + Ryujinx.Graphics.Shader/QueryInfoName.cs | 3 +- .../StructuredIr/AstTextureOperation.cs | 7 +- .../StructuredIr/StructuredProgram.cs | 6 + Ryujinx.Graphics.Shader/TextureFormat.cs | 128 +++++++++ .../Translation/ShaderConfig.cs | 2 + 15 files changed, 620 insertions(+), 109 deletions(-) create mode 100644 Ryujinx.Graphics.Shader/TextureFormat.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index fc257f99..920cf0dd 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -29,7 +29,26 @@ namespace Ryujinx.Graphics.Gpu.Engine int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize); + uint sbEnableMask = 0; + uint ubEnableMask = 0; + + for (int index = 0; index < Constants.TotalCpUniformBuffers; index++) + { + if (!qmd.ConstantBufferValid(index)) + { + continue; + } + + ubEnableMask |= 1u << index; + + ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32; + ulong size = (ulong)qmd.ConstantBufferSize(index); + + BufferManager.SetComputeUniformBuffer(index, gpuVa, size); + } + ComputeShader cs = ShaderCache.GetComputeShader( + state, shaderGpuVa, qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, @@ -49,25 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Engine TextureManager.SetComputeTextureBufferIndex(state.Get(MethodOffset.TextureBufferIndex)); - ShaderProgramInfo info = cs.Shader.Program.Info; - - uint sbEnableMask = 0; - uint ubEnableMask = 0; - - for (int index = 0; index < Constants.TotalCpUniformBuffers; index++) - { - if (!qmd.ConstantBufferValid(index)) - { - continue; - } - - ubEnableMask |= 1u << index; - - ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32; - ulong size = (ulong)qmd.ConstantBufferSize(index); - - BufferManager.SetComputeUniformBuffer(index, gpuVa, size); - } + ShaderProgramInfo info = cs.Shader.Program.Info; for (int index = 0; index < info.CBuffers.Count; index++) { diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 27cbc6ff..b127690b 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -207,6 +207,17 @@ namespace Ryujinx.Graphics.Gpu.Image UpdateRenderTargets(); } + /// + /// Gets a texture descriptor used on the compute pipeline. + /// + /// Current GPU state + /// Shader "fake" handle of the texture + /// The texture descriptor + public TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle) + { + return _cpBindingsManager.GetTextureDescriptor(state, 0, handle); + } + /// /// Gets a texture descriptor used on the graphics pipeline. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 7ad00f32..6bbc3b11 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -50,6 +50,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// This automatically translates, compiles and adds the code to the cache if not present. /// + /// Current GPU state /// GPU virtual address of the binary shader code /// Local group size X of the computer shader /// Local group size Y of the computer shader @@ -58,6 +59,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shared memory size of the compute shader /// Compiled compute shader code public ComputeShader GetComputeShader( + GpuState state, ulong gpuVa, int localSizeX, int localSizeY, @@ -79,6 +81,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } CachedShader shader = TranslateComputeShader( + state, gpuVa, localSizeX, localSizeY, @@ -241,6 +244,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Translates the binary Maxwell shader code to something that the host API accepts. /// + /// Current GPU state /// GPU virtual address of the binary shader code /// Local group size X of the computer shader /// Local group size Y of the computer shader @@ -249,6 +253,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shared memory size of the compute shader /// Compiled compute shader code private CachedShader TranslateComputeShader( + GpuState state, ulong gpuVa, int localSizeX, int localSizeY, @@ -265,12 +270,20 @@ namespace Ryujinx.Graphics.Gpu.Shader { return info switch { - QueryInfoName.ComputeLocalSizeX => localSizeX, - QueryInfoName.ComputeLocalSizeY => localSizeY, - QueryInfoName.ComputeLocalSizeZ => localSizeZ, - QueryInfoName.ComputeLocalMemorySize => localMemorySize, - QueryInfoName.ComputeSharedMemorySize => sharedMemorySize, - _ => QueryInfoCommon(info) + QueryInfoName.ComputeLocalSizeX + => localSizeX, + QueryInfoName.ComputeLocalSizeY + => localSizeY, + QueryInfoName.ComputeLocalSizeZ + => localSizeZ, + QueryInfoName.ComputeLocalMemorySize + => localMemorySize, + QueryInfoName.ComputeSharedMemorySize + => sharedMemorySize, + QueryInfoName.TextureFormat + => (int)QueryComputeTextureFormat(state, index), + _ + => QueryInfoCommon(info) }; } @@ -317,10 +330,16 @@ namespace Ryujinx.Graphics.Gpu.Shader { return info switch { - QueryInfoName.IsTextureBuffer => Convert.ToInt32(QueryIsTextureBuffer(state, (int)stage - 1, index)), - QueryInfoName.IsTextureRectangle => Convert.ToInt32(QueryIsTextureRectangle(state, (int)stage - 1, index)), - QueryInfoName.PrimitiveTopology => (int)GetPrimitiveTopology(), - _ => QueryInfoCommon(info) + QueryInfoName.IsTextureBuffer + => Convert.ToInt32(QueryIsTextureBuffer(state, (int)stage - 1, index)), + QueryInfoName.IsTextureRectangle + => Convert.ToInt32(QueryIsTextureRectangle(state, (int)stage - 1, index)), + QueryInfoName.PrimitiveTopology + => (int)QueryPrimitiveTopology(), + QueryInfoName.TextureFormat + => (int)QueryGraphicsTextureFormat(state, (int)stage - 1, index), + _ + => QueryInfoCommon(info) }; } @@ -378,7 +397,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// This is required by geometry shaders. /// /// Primitive topology - private InputTopology GetPrimitiveTopology() + private InputTopology QueryPrimitiveTopology() { switch (_context.Methods.PrimitiveType) { @@ -414,7 +433,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// True if the texture is a buffer texture, false otherwise private bool QueryIsTextureBuffer(GpuState state, int stageIndex, int index) { - return GetTextureDescriptor(state, stageIndex, index).UnpackTextureTarget() == TextureTarget.TextureBuffer; + return GetGraphicsTextureDescriptor(state, stageIndex, index).UnpackTextureTarget() == TextureTarget.TextureBuffer; } /// @@ -428,7 +447,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// True if the texture is a rectangle texture, false otherwise private bool QueryIsTextureRectangle(GpuState state, int stageIndex, int index) { - var descriptor = GetTextureDescriptor(state, stageIndex, index); + var descriptor = GetGraphicsTextureDescriptor(state, stageIndex, index); TextureTarget target = descriptor.UnpackTextureTarget(); @@ -439,15 +458,106 @@ namespace Ryujinx.Graphics.Gpu.Shader } /// - /// Gets the texture descriptor for a given texture on the pool. + /// Queries the format of a given texture. + /// + /// Current GPU state + /// Index of the texture (this is the shader "fake" handle) + /// The texture format + private TextureFormat QueryComputeTextureFormat(GpuState state, int index) + { + return QueryTextureFormat(GetComputeTextureDescriptor(state, index)); + } + + /// + /// Queries the format of a given texture. /// /// Current GPU state /// Index of the shader stage /// Index of the texture (this is the shader "fake" handle) - /// Texture descriptor - private TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int index) + /// The texture format + private TextureFormat QueryGraphicsTextureFormat(GpuState state, int stageIndex, int index) { - return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(state, stageIndex, index); + return QueryTextureFormat(GetGraphicsTextureDescriptor(state, stageIndex, index)); + } + + /// + /// Queries the format of a given texture. + /// + /// Descriptor of the texture from the texture pool + /// The texture format + private static TextureFormat QueryTextureFormat(TextureDescriptor descriptor) + { + if (!FormatTable.TryGetTextureFormat(descriptor.UnpackFormat(), descriptor.UnpackSrgb(), out FormatInfo formatInfo)) + { + return TextureFormat.Unknown; + } + + return formatInfo.Format switch + { + Format.R8Unorm => TextureFormat.R8Unorm, + Format.R8Snorm => TextureFormat.R8Snorm, + Format.R8Uint => TextureFormat.R8Uint, + Format.R8Sint => TextureFormat.R8Sint, + Format.R16Float => TextureFormat.R16Float, + Format.R16Unorm => TextureFormat.R16Unorm, + Format.R16Snorm => TextureFormat.R16Snorm, + Format.R16Uint => TextureFormat.R16Uint, + Format.R16Sint => TextureFormat.R16Sint, + Format.R32Float => TextureFormat.R32Float, + Format.R32Uint => TextureFormat.R32Uint, + Format.R32Sint => TextureFormat.R32Sint, + Format.R8G8Unorm => TextureFormat.R8G8Unorm, + Format.R8G8Snorm => TextureFormat.R8G8Snorm, + Format.R8G8Uint => TextureFormat.R8G8Uint, + Format.R8G8Sint => TextureFormat.R8G8Sint, + Format.R16G16Float => TextureFormat.R16G16Float, + Format.R16G16Unorm => TextureFormat.R16G16Unorm, + Format.R16G16Snorm => TextureFormat.R16G16Snorm, + Format.R16G16Uint => TextureFormat.R16G16Uint, + Format.R16G16Sint => TextureFormat.R16G16Sint, + Format.R32G32Float => TextureFormat.R32G32Float, + Format.R32G32Uint => TextureFormat.R32G32Uint, + Format.R32G32Sint => TextureFormat.R32G32Sint, + Format.R8G8B8A8Unorm => TextureFormat.R8G8B8A8Unorm, + Format.R8G8B8A8Snorm => TextureFormat.R8G8B8A8Snorm, + Format.R8G8B8A8Uint => TextureFormat.R8G8B8A8Uint, + Format.R8G8B8A8Sint => TextureFormat.R8G8B8A8Sint, + Format.R16G16B16A16Float => TextureFormat.R16G16B16A16Float, + Format.R16G16B16A16Unorm => TextureFormat.R16G16B16A16Unorm, + Format.R16G16B16A16Snorm => TextureFormat.R16G16B16A16Snorm, + Format.R16G16B16A16Uint => TextureFormat.R16G16B16A16Uint, + Format.R16G16B16A16Sint => TextureFormat.R16G16B16A16Sint, + Format.R32G32B32A32Float => TextureFormat.R32G32B32A32Float, + Format.R32G32B32A32Uint => TextureFormat.R32G32B32A32Uint, + Format.R32G32B32A32Sint => TextureFormat.R32G32B32A32Sint, + Format.R10G10B10A2Unorm => TextureFormat.R10G10B10A2Unorm, + Format.R10G10B10A2Uint => TextureFormat.R10G10B10A2Uint, + Format.R11G11B10Float => TextureFormat.R11G11B10Float, + _ => TextureFormat.Unknown + }; + } + + /// + /// Gets the texture descriptor for a given texture on the pool. + /// + /// Current GPU state + /// Index of the texture (this is the shader "fake" handle) + /// Texture descriptor + private TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle) + { + return _context.Methods.TextureManager.GetComputeTextureDescriptor(state, handle); + } + + /// + /// Gets the texture descriptor for a given texture on the pool. + /// + /// Current GPU state + /// Index of the shader stage + /// Index of the texture (this is the shader "fake" handle) + /// Texture descriptor + private TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle) + { + return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(state, stageIndex, handle); } /// @@ -459,9 +569,12 @@ namespace Ryujinx.Graphics.Gpu.Shader { return info switch { - QueryInfoName.StorageBufferOffsetAlignment => _context.Capabilities.StorageBufferOffsetAlignment, - QueryInfoName.SupportsNonConstantTextureOffset => Convert.ToInt32(_context.Capabilities.SupportsNonConstantTextureOffset), - _ => 0 + QueryInfoName.StorageBufferOffsetAlignment + => _context.Capabilities.StorageBufferOffsetAlignment, + QueryInfoName.SupportsNonConstantTextureOffset + => Convert.ToInt32(_context.Capabilities.SupportsNonConstantTextureOffset), + _ + => 0 }; } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index b1291906..36aff3fc 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -326,9 +326,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl continue; } - string imageTypeName = GetImageTypeName(texOp.Type); + string layout = texOp.Format.ToGlslFormat(); - context.AppendLine("writeonly uniform " + imageTypeName + " " + imageName + ";"); + if (!string.IsNullOrEmpty(layout)) + { + layout = "layout(" + layout + ") "; + } + + string imageTypeName = GetImageTypeName(texOp.Type, texOp.Format.GetComponentType()); + + context.AppendLine("uniform " + layout + imageTypeName + " " + imageName + ";"); } foreach (KeyValuePair kv in images) @@ -455,7 +462,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return typeName; } - private static string GetImageTypeName(SamplerType type) + private static string GetImageTypeName(SamplerType type, VariableType componentType) { string typeName; @@ -480,6 +487,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl typeName += "Array"; } + switch (componentType) + { + case VariableType.U32: typeName = 'u' + typeName; break; + case VariableType.S32: typeName = 'i' + typeName; break; + } + return typeName; } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index fe982770..f1537c3d 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -4,6 +4,7 @@ using System; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenMemory; +using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenPacking; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions @@ -115,53 +116,56 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { switch (inst) { + case Instruction.ImageLoad: + return ImageLoadOrStore(context, operation); + case Instruction.ImageStore: - return InstGenMemory.ImageStore(context, operation); + return ImageLoadOrStore(context, operation); case Instruction.LoadAttribute: - return InstGenMemory.LoadAttribute(context, operation); + return LoadAttribute(context, operation); case Instruction.LoadConstant: - return InstGenMemory.LoadConstant(context, operation); + return LoadConstant(context, operation); case Instruction.LoadLocal: - return InstGenMemory.LoadLocal(context, operation); + return LoadLocal(context, operation); case Instruction.LoadShared: - return InstGenMemory.LoadShared(context, operation); + return LoadShared(context, operation); case Instruction.LoadStorage: - return InstGenMemory.LoadStorage(context, operation); + return LoadStorage(context, operation); case Instruction.Lod: - return InstGenMemory.Lod(context, operation); + return Lod(context, operation); case Instruction.PackDouble2x32: - return InstGenPacking.PackDouble2x32(context, operation); + return PackDouble2x32(context, operation); case Instruction.PackHalf2x16: - return InstGenPacking.PackHalf2x16(context, operation); + return PackHalf2x16(context, operation); case Instruction.StoreLocal: - return InstGenMemory.StoreLocal(context, operation); + return StoreLocal(context, operation); case Instruction.StoreShared: - return InstGenMemory.StoreShared(context, operation); + return StoreShared(context, operation); case Instruction.StoreStorage: - return InstGenMemory.StoreStorage(context, operation); + return StoreStorage(context, operation); case Instruction.TextureSample: - return InstGenMemory.TextureSample(context, operation); + return TextureSample(context, operation); case Instruction.TextureSize: - return InstGenMemory.TextureSize(context, operation); + return TextureSize(context, operation); case Instruction.UnpackDouble2x32: - return InstGenPacking.UnpackDouble2x32(context, operation); + return UnpackDouble2x32(context, operation); case Instruction.UnpackHalf2x16: - return InstGenPacking.UnpackHalf2x16(context, operation); + return UnpackHalf2x16(context, operation); } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 74828702..d05c77df 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -9,16 +9,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { static class InstGenMemory { - public static string ImageStore(CodeGenContext context, AstOperation operation) + public static string ImageLoadOrStore(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool isArray = (texOp.Type & SamplerType.Array) != 0; + bool isArray = (texOp.Type & SamplerType.Array) != 0; bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0; - string texCall = "imageStore"; + string texCall = texOp.Inst == Instruction.ImageLoad ? "imageLoad" : "imageStore"; int srcIndex = isBindless ? 1 : 0; @@ -40,14 +40,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions int coordsCount = texOp.Type.GetDimensions(); - int pCount = coordsCount; - - int arrayIndexElem = -1; - - if (isArray) - { - arrayIndexElem = pCount++; - } + int pCount = coordsCount + (isArray ? 1 : 0); void Append(string str) { @@ -70,23 +63,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Append(Src(VariableType.S32)); } - string[] cElems = new string[4]; - - for (int index = 0; index < 4; index++) + if (texOp.Inst == Instruction.ImageStore) { - if (srcIndex < texOp.SourcesCount) + VariableType type = texOp.Format.GetComponentType(); + + string[] cElems = new string[4]; + + for (int index = 0; index < 4; index++) { - cElems[index] = Src(VariableType.F32); + if (srcIndex < texOp.SourcesCount) + { + cElems[index] = Src(type); + } + else + { + cElems[index] = type switch + { + VariableType.S32 => NumberFormatter.FormatInt(0), + VariableType.U32 => NumberFormatter.FormatUint(0), + _ => NumberFormatter.FormatFloat(0) + }; + } } - else + + string prefix = type switch { - cElems[index] = NumberFormatter.FormatFloat(0); - } + VariableType.S32 => "i", + VariableType.U32 => "u", + _ => string.Empty + }; + + Append(prefix + "vec4(" + string.Join(", ", cElems) + ")"); } - Append("vec4(" + string.Join(", ", cElems) + ")"); - - texCall += ")"; + texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMask(texOp.Index) : ""); return texCall; } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 8d092d4e..e9a37d87 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -280,6 +280,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { return GetOperandVarType((AstOperand)operation.GetSource(0)); } + else if (operation is AstTextureOperation texOp && + (texOp.Inst == Instruction.ImageLoad || + texOp.Inst == Instruction.ImageStore)) + { + return texOp.Format.GetComponentType(); + } return GetDestVarType(operation.Inst); } diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs index 2f46ee32..02a8be01 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs @@ -219,6 +219,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("1110111101010x", InstEmit.St, typeof(OpCodeMemory)); Set("1110111011011x", InstEmit.Stg, typeof(OpCodeMemory)); Set("1110111101011x", InstEmit.Sts, typeof(OpCodeMemory)); + Set("11101011000xxx", InstEmit.Suld, typeof(OpCodeImage)); Set("11101011001xxx", InstEmit.Sust, typeof(OpCodeImage)); Set("1111000011111x", InstEmit.Sync, typeof(OpCodeBranchPop)); Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex)); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 7b9794ea..0ff2740b 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -4,12 +4,152 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; namespace Ryujinx.Graphics.Shader.Instructions { static partial class InstEmit { + public static void Suld(EmitterContext context) + { + OpCodeImage op = (OpCodeImage)context.CurrOp; + + SamplerType type = ConvertSamplerType(op.Dimensions); + + if (type == SamplerType.None) + { + context.Config.PrintLog("Invalid image store sampler type."); + + return; + } + + // Rb is Rd on the SULD instruction. + int rdIndex = op.Rb.Index; + int raIndex = op.Ra.Index; + + Operand Ra() + { + if (raIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(raIndex++, RegisterType.Gpr)); + } + + bool isArray = op.Dimensions == ImageDimensions.Image1DArray || + op.Dimensions == ImageDimensions.Image2DArray; + + Operand arrayIndex = isArray ? Ra() : null; + + List sourcesList = new List(); + + if (op.IsBindless) + { + sourcesList.Add(context.Copy(Register(op.Rc))); + } + + int coordsCount = type.GetDimensions(); + + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(Ra()); + } + + if (isArray) + { + sourcesList.Add(arrayIndex); + + type |= SamplerType.Array; + } + + Operand[] sources = sourcesList.ToArray(); + + int handle = !op.IsBindless ? op.Immediate : 0; + + TextureFlags flags = op.IsBindless ? TextureFlags.Bindless : TextureFlags.None; + + if (op.UseComponents) + { + int componentMask = (int)op.Components; + + for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) + { + if ((compMask & 1) == 0) + { + continue; + } + + if (rdIndex == RegisterConsts.RegisterZeroIndex) + { + break; + } + + Operand rd = Register(rdIndex++, RegisterType.Gpr); + + TextureOperation operation = new TextureOperation( + Instruction.ImageLoad, + type, + flags, + handle, + compIndex, + rd, + sources); + + if (!op.IsBindless) + { + operation.Format = GetTextureFormat(context, handle); + } + + context.Add(operation); + } + } + else + { + if (op.ByteAddress) + { + int xIndex = op.IsBindless ? 1 : 0; + + sources[xIndex] = context.ShiftRightS32(sources[xIndex], Const(GetComponentSizeInBytesLog2(op.Size))); + } + + int components = GetComponents(op.Size); + + for (int compIndex = 0; compIndex < components; compIndex++) + { + if (rdIndex == RegisterConsts.RegisterZeroIndex) + { + break; + } + + Operand rd = Register(rdIndex++, RegisterType.Gpr); + + TextureOperation operation = new TextureOperation( + Instruction.ImageLoad, + type, + flags, + handle, + compIndex, + rd, + sources) + { + Format = GetTextureFormat(op.Size) + }; + + context.Add(operation); + + switch (op.Size) + { + case IntegerSize.U8: context.Copy(rd, ZeroExtendTo32(context, rd, 8)); break; + case IntegerSize.U16: context.Copy(rd, ZeroExtendTo32(context, rd, 16)); break; + case IntegerSize.S8: context.Copy(rd, SignExtendTo32(context, rd, 8)); break; + case IntegerSize.S16: context.Copy(rd, SignExtendTo32(context, rd, 16)); break; + } + } + } + } + public static void Sust(EmitterContext context) { OpCodeImage op = (OpCodeImage)context.CurrOp; @@ -72,6 +212,8 @@ namespace Ryujinx.Graphics.Shader.Instructions type |= SamplerType.Array; } + TextureFormat format = TextureFormat.Unknown; + if (op.UseComponents) { int componentMask = (int)op.Components; @@ -83,12 +225,33 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(Rb()); } } + + if (!op.IsBindless) + { + format = GetTextureFormat(context, op.Immediate); + } } else { - context.Config.PrintLog("Unsized image store not supported."); + if (op.ByteAddress) + { + int xIndex = op.IsBindless ? 1 : 0; + + sourcesList[xIndex] = context.ShiftRightS32(sourcesList[xIndex], Const(GetComponentSizeInBytesLog2(op.Size))); + } + + int components = GetComponents(op.Size); + + for (int compIndex = 0; compIndex < components; compIndex++) + { + sourcesList.Add(Rb()); + } + + format = GetTextureFormat(op.Size); } + System.Console.WriteLine(format.ToString()); + Operand[] sources = sourcesList.ToArray(); int handle = !op.IsBindless ? op.Immediate : 0; @@ -102,7 +265,10 @@ namespace Ryujinx.Graphics.Shader.Instructions handle, 0, null, - sources); + sources) + { + Format = format + }; context.Add(operation); } @@ -880,43 +1046,87 @@ namespace Ryujinx.Graphics.Shader.Instructions } } - private static SamplerType ConvertSamplerType(ImageDimensions target) + private static int GetComponents(IntegerSize size) { - switch (target) + return size switch { - case ImageDimensions.Image1D: - return SamplerType.Texture1D; + IntegerSize.B64 => 2, + IntegerSize.B128 => 4, + IntegerSize.UB128 => 4, + _ => 1 + }; + } - case ImageDimensions.ImageBuffer: - return SamplerType.TextureBuffer; + private static int GetComponentSizeInBytesLog2(IntegerSize size) + { + return size switch + { + IntegerSize.U8 => 0, + IntegerSize.S8 => 0, + IntegerSize.U16 => 1, + IntegerSize.S16 => 1, + IntegerSize.B32 => 2, + IntegerSize.B64 => 3, + IntegerSize.B128 => 4, + IntegerSize.UB128 => 4, + _ => 2 + }; + } - case ImageDimensions.Image1DArray: - return SamplerType.Texture1D | SamplerType.Array; + private static TextureFormat GetTextureFormat(EmitterContext context, int handle) + { + var format = (TextureFormat)context.Config.QueryInfo(QueryInfoName.TextureFormat, handle); - case ImageDimensions.Image2D: - return SamplerType.Texture2D; + if (format == TextureFormat.Unknown) + { + context.Config.PrintLog($"Unknown format for texture {handle}."); - case ImageDimensions.Image2DArray: - return SamplerType.Texture2D | SamplerType.Array; - - case ImageDimensions.Image3D: - return SamplerType.Texture3D; + format = TextureFormat.R8G8B8A8Unorm; } - return SamplerType.None; + return format; + } + + private static TextureFormat GetTextureFormat(IntegerSize size) + { + return size switch + { + IntegerSize.U8 => TextureFormat.R8Uint, + IntegerSize.S8 => TextureFormat.R8Sint, + IntegerSize.U16 => TextureFormat.R16Uint, + IntegerSize.S16 => TextureFormat.R16Sint, + IntegerSize.B32 => TextureFormat.R32Uint, + IntegerSize.B64 => TextureFormat.R32G32Uint, + IntegerSize.B128 => TextureFormat.R32G32B32A32Uint, + IntegerSize.UB128 => TextureFormat.R32G32B32A32Uint, + _ => TextureFormat.R32Uint + }; + } + + private static SamplerType ConvertSamplerType(ImageDimensions target) + { + return target switch + { + ImageDimensions.Image1D => SamplerType.Texture1D, + ImageDimensions.ImageBuffer => SamplerType.TextureBuffer, + ImageDimensions.Image1DArray => SamplerType.Texture1D | SamplerType.Array, + ImageDimensions.Image2D => SamplerType.Texture2D, + ImageDimensions.Image2DArray => SamplerType.Texture2D | SamplerType.Array, + ImageDimensions.Image3D => SamplerType.Texture3D, + _ => SamplerType.None + }; } private static SamplerType ConvertSamplerType(TextureDimensions dimensions) { - switch (dimensions) + return dimensions switch { - case TextureDimensions.Texture1D: return SamplerType.Texture1D; - case TextureDimensions.Texture2D: return SamplerType.Texture2D; - case TextureDimensions.Texture3D: return SamplerType.Texture3D; - case TextureDimensions.TextureCube: return SamplerType.TextureCube; - } - - throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\"."); + TextureDimensions.Texture1D => SamplerType.Texture1D, + TextureDimensions.Texture2D => SamplerType.Texture2D, + TextureDimensions.Texture3D => SamplerType.Texture3D, + TextureDimensions.TextureCube => SamplerType.TextureCube, + _ => throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".") + }; } private static SamplerType ConvertSamplerType(TextureTarget type) diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index 718d2c2e..06541f90 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public int Handle { get; private set; } + public TextureFormat Format { get; set; } + public TextureOperation( Instruction inst, SamplerType type, diff --git a/Ryujinx.Graphics.Shader/QueryInfoName.cs b/Ryujinx.Graphics.Shader/QueryInfoName.cs index 887c0d7d..41f42cbd 100644 --- a/Ryujinx.Graphics.Shader/QueryInfoName.cs +++ b/Ryujinx.Graphics.Shader/QueryInfoName.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Shader IsTextureRectangle, PrimitiveTopology, StorageBufferOffsetAlignment, - SupportsNonConstantTextureOffset + SupportsNonConstantTextureOffset, + TextureFormat } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index 5473978e..a3fa3e3a 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -4,8 +4,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { class AstTextureOperation : AstOperation { - public SamplerType Type { get; } - public TextureFlags Flags { get; } + public SamplerType Type { get; } + public TextureFormat Format { get; } + public TextureFlags Flags { get; } public int Handle { get; } public int ArraySize { get; } @@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public AstTextureOperation( Instruction inst, SamplerType type, + TextureFormat format, TextureFlags flags, int handle, int arraySize, @@ -20,6 +22,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr params IAstNode[] sources) : base(inst, index, sources) { Type = type; + Format = format; Flags = flags; Handle = handle; ArraySize = arraySize; diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 4758b08f..f1dd08f2 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -57,6 +57,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr return new AstTextureOperation( inst, texOp.Type, + texOp.Format, texOp.Flags, texOp.Handle, 4, // TODO: Non-hardcoded array size. @@ -118,6 +119,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (operation is TextureOperation texOp) { + if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore) + { + dest.VarType = texOp.Format.GetComponentType(); + } + AstTextureOperation astTexOp = GetAstTextureOperation(texOp); if (texOp.Inst == Instruction.ImageLoad) diff --git a/Ryujinx.Graphics.Shader/TextureFormat.cs b/Ryujinx.Graphics.Shader/TextureFormat.cs new file mode 100644 index 00000000..fb4e5b03 --- /dev/null +++ b/Ryujinx.Graphics.Shader/TextureFormat.cs @@ -0,0 +1,128 @@ +using Ryujinx.Graphics.Shader.StructuredIr; + +namespace Ryujinx.Graphics.Shader +{ + public enum TextureFormat + { + Unknown, + R8Unorm, + R8Snorm, + R8Uint, + R8Sint, + R16Float, + R16Unorm, + R16Snorm, + R16Uint, + R16Sint, + R32Float, + R32Uint, + R32Sint, + R8G8Unorm, + R8G8Snorm, + R8G8Uint, + R8G8Sint, + R16G16Float, + R16G16Unorm, + R16G16Snorm, + R16G16Uint, + R16G16Sint, + R32G32Float, + R32G32Uint, + R32G32Sint, + R8G8B8A8Unorm, + R8G8B8A8Snorm, + R8G8B8A8Uint, + R8G8B8A8Sint, + R16G16B16A16Float, + R16G16B16A16Unorm, + R16G16B16A16Snorm, + R16G16B16A16Uint, + R16G16B16A16Sint, + R32G32B32A32Float, + R32G32B32A32Uint, + R32G32B32A32Sint, + R10G10B10A2Unorm, + R10G10B10A2Uint, + R11G11B10Float + } + + static class TextureFormatExtensions + { + public static string ToGlslFormat(this TextureFormat format) + { + return format switch + { + TextureFormat.R8Unorm => "r8", + TextureFormat.R8Snorm => "r8_snorm", + TextureFormat.R8Uint => "r8ui", + TextureFormat.R8Sint => "r8i", + TextureFormat.R16Float => "r16f", + TextureFormat.R16Unorm => "r16", + TextureFormat.R16Snorm => "r16_snorm", + TextureFormat.R16Uint => "r16ui", + TextureFormat.R16Sint => "r16i", + TextureFormat.R32Float => "r32f", + TextureFormat.R32Uint => "r32ui", + TextureFormat.R32Sint => "r32i", + TextureFormat.R8G8Unorm => "rg8", + TextureFormat.R8G8Snorm => "rg8_snorm", + TextureFormat.R8G8Uint => "rg8ui", + TextureFormat.R8G8Sint => "rg8i", + TextureFormat.R16G16Float => "rg16f", + TextureFormat.R16G16Unorm => "rg16", + TextureFormat.R16G16Snorm => "rg16_snorm", + TextureFormat.R16G16Uint => "rg16ui", + TextureFormat.R16G16Sint => "rg16i", + TextureFormat.R32G32Float => "rg32f", + TextureFormat.R32G32Uint => "rg32ui", + TextureFormat.R32G32Sint => "rg32i", + TextureFormat.R8G8B8A8Unorm => "rgba8", + TextureFormat.R8G8B8A8Snorm => "rgba8_snorm", + TextureFormat.R8G8B8A8Uint => "rgba8ui", + TextureFormat.R8G8B8A8Sint => "rgba8i", + TextureFormat.R16G16B16A16Float => "rgba16f", + TextureFormat.R16G16B16A16Unorm => "rgba16", + TextureFormat.R16G16B16A16Snorm => "rgba16_snorm", + TextureFormat.R16G16B16A16Uint => "rgba16ui", + TextureFormat.R16G16B16A16Sint => "rgba16i", + TextureFormat.R32G32B32A32Float => "rgba32f", + TextureFormat.R32G32B32A32Uint => "rgba32ui", + TextureFormat.R32G32B32A32Sint => "rgba32i", + TextureFormat.R10G10B10A2Unorm => "rgb10_a2", + TextureFormat.R10G10B10A2Uint => "rgb10_a2ui", + TextureFormat.R11G11B10Float => "r11f_g11f_b10f", + _ => string.Empty + }; + } + + public static VariableType GetComponentType(this TextureFormat format) + { + switch (format) + { + case TextureFormat.R8Uint: + case TextureFormat.R16Uint: + case TextureFormat.R32Uint: + case TextureFormat.R8G8Uint: + case TextureFormat.R16G16Uint: + case TextureFormat.R32G32Uint: + case TextureFormat.R8G8B8A8Uint: + case TextureFormat.R16G16B16A16Uint: + case TextureFormat.R32G32B32A32Uint: + case TextureFormat.R10G10B10A2Uint: + return VariableType.U32; + case TextureFormat.R8Sint: + case TextureFormat.R16Sint: + case TextureFormat.R32Sint: + case TextureFormat.R8G8Sint: + case TextureFormat.R16G16Sint: + case TextureFormat.R32G32Sint: + case TextureFormat.R8G8B8A8Sint: + case TextureFormat.R16G16B16A16Sint: + case TextureFormat.R32G32B32A32Sint: + return VariableType.S32; + }; + + return VariableType.F32; + } + } +} diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 60660847..3035ebf6 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -102,6 +102,8 @@ namespace Ryujinx.Graphics.Shader.Translation return 16; case QueryInfoName.SupportsNonConstantTextureOffset: return Convert.ToInt32(true); + case QueryInfoName.TextureFormat: + return (int)TextureFormat.R8G8B8A8Unorm; } }