diff options
| author | gdk <gab.dark.100@gmail.com> | 2019-11-08 17:29:41 -0300 |
|---|---|---|
| committer | Thog <thog@protonmail.com> | 2020-01-09 02:13:00 +0100 |
| commit | 769c02235f489f02b1791e6e76dc8b3ab18028ee (patch) | |
| tree | ef0a2ffc5030360d5cef78e7c67e131e44348d50 /Ryujinx.Graphics.Shader | |
| parent | 1e8bc29f32cde08616175f8f87405dfa7b8c4025 (diff) | |
Add ATOMS, LDS, POPC, RED, STS and VOTE shader instructions, start changing the way how global memory is handled
Diffstat (limited to 'Ryujinx.Graphics.Shader')
41 files changed, 943 insertions, 184 deletions
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Constants.cs b/Ryujinx.Graphics.Shader/CodeGen/Constants.cs new file mode 100644 index 00000000..10c22c60 --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Constants.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Shader.CodeGen +{ + static class Constants + { + public const int MaxShaderStorageBuffers = 16; + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 6c4ba949..a8cabaaf 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -16,7 +16,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public static void Declare(CodeGenContext context, StructuredProgramInfo info) { context.AppendLine("#version 420 core"); + context.AppendLine("#extension GL_ARB_gpu_shader_int64 : enable"); context.AppendLine("#extension GL_ARB_shader_ballot : enable"); + context.AppendLine("#extension GL_ARB_shader_group_vote : enable"); context.AppendLine("#extension GL_ARB_shader_storage_buffer_object : enable"); if (context.Config.Stage == ShaderStage.Compute) @@ -66,9 +68,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(); - context.AppendLine($"precise float {DefaultNames.LocalMemoryName}[0x100];"); + context.AppendLine($"uint {DefaultNames.LocalMemoryName}[0x100];"); context.AppendLine(); + if (context.Config.Stage == ShaderStage.Compute) + { + context.AppendLine($"shared uint {DefaultNames.SharedMemoryName}[0x100];"); + context.AppendLine(); + } + if (info.CBuffers.Count != 0) { DeclareUniforms(context, info); @@ -78,7 +86,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (info.SBuffers.Count != 0) { - DeclareStorage(context, info); + DeclareUsedStorage(context, info); context.AppendLine(); } @@ -168,6 +176,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); } + + if ((info.HelperFunctionsMask & HelperFunctionsMask.GlobalMemory) != 0) + { + context.AppendLine($"ivec2 {DefaultNames.GmemOffsetName};"); + } } private static string GetVarTypeName(VariableType type) @@ -205,24 +218,59 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } - private static void DeclareStorage(CodeGenContext context, StructuredProgramInfo info) + private static void DeclareAllStorage(CodeGenContext context, StructuredProgramInfo info) { - foreach (int sbufSlot in info.SBuffers.OrderBy(x => x)) + string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage); + + sbName += "_" + DefaultNames.StorageNamePrefix; + + string blockName = $"{sbName}_{DefaultNames.BlockSuffix}"; + + context.AppendLine("layout (std430) buffer " + blockName); + + context.EnterScope(); + + context.AppendLine("uint " + DefaultNames.DataName + "[];"); + + string arraySize = NumberFormatter.FormatInt(Constants.MaxShaderStorageBuffers); + + context.LeaveScope($" {sbName}[{arraySize}];"); + + for (int sbufSlot = 0; sbufSlot < Constants.MaxShaderStorageBuffers; sbufSlot++) { - string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage); + context.SBufferDescriptors.Add(new BufferDescriptor($"{blockName}[{sbufSlot}]", sbufSlot)); + } + } - sbName += "_" + DefaultNames.StorageNamePrefix + sbufSlot; + private static void DeclareUsedStorage(CodeGenContext context, StructuredProgramInfo info) + { + string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage); - context.SBufferDescriptors.Add(new BufferDescriptor(sbName, sbufSlot)); + sbName += "_" + DefaultNames.StorageNamePrefix; - context.AppendLine("layout (std430) buffer " + sbName); + string blockName = $"{sbName}_{DefaultNames.BlockSuffix}"; - context.EnterScope(); + int maxSlot = 0; - context.AppendLine("precise float " + OperandManager.GetSbName(context.Config.Stage, sbufSlot) + "[];"); + foreach (int sbufSlot in info.SBuffers) + { + context.SBufferDescriptors.Add(new BufferDescriptor($"{blockName}[{sbufSlot}]", sbufSlot)); - context.LeaveScope(";"); + if (maxSlot < sbufSlot) + { + maxSlot = sbufSlot; + } } + + context.AppendLine("layout (std430) buffer " + blockName); + + context.EnterScope(); + + context.AppendLine("uint " + DefaultNames.DataName + "[];"); + + string arraySize = NumberFormatter.FormatInt(maxSlot + 1); + + context.LeaveScope($" {sbName}[{arraySize}];"); } private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo info) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index a06b0cc8..f1abc949 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -11,12 +11,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string OAttributePrefix = "out_attr"; public const string StorageNamePrefix = "s"; - public const string StorageNameSuffix = "data"; + + public const string DataName = "data"; + + public const string BlockSuffix = "block"; public const string UniformNamePrefix = "c"; public const string UniformNameSuffix = "data"; - public const string LocalMemoryName = "local_mem"; + public const string LocalMemoryName = "local_mem"; + public const string SharedMemoryName = "shared_mem"; + + public const string GmemOffsetName = "gmemOffset"; public const string UndefinedName = "undef"; } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/GlobalMemory.glsl b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/GlobalMemory.glsl new file mode 100644 index 00000000..b8544ae2 --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/GlobalMemory.glsl @@ -0,0 +1,18 @@ +ivec2 Helper_GetStorageBuffer(uint aLow, uint aHigh) +{ + uint64_t address = packUint2x32(uvec2(aLow, aHigh)); + int i; + for (i = 0; i < 16; i++) + { + int offset = 0x40 + i * 4; + uint baseLow = fp_c0_data[offset]; + uint baseHigh = fp_c0_data[offset + 1]; + uint size = fp_c0_data[offset + 2]; + uint64_t baseAddr = packUint2x32(uvec2(baseLow, baseHigh)); + if (address >= baseAddr && address < baseAddr + packUint2x32(uvec2(size, 0))) + { + return ivec2(i, int(unpackUint2x32(address - (baseAddr & ~63ul)).x) >> 2); + } + } + return ivec2(0); +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs index f1540fbf..302b56ad 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/HelperFunctionNames.cs @@ -2,6 +2,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { static class HelperFunctionNames { + public static string GetStorageBuffer = "Helper_GetStorageBuffer"; + public static string Shuffle = "Helper_Shuffle"; public static string ShuffleDown = "Helper_ShuffleDown"; public static string ShuffleUp = "Helper_ShuffleUp"; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 3bf31c16..b5cab54e 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.Shader.StructuredIr; 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.StructuredIr.InstructionInfo; namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions @@ -31,6 +32,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if ((info.Type & InstType.Call) != 0) { + bool atomic = (info.Type & InstType.Atomic) != 0; + int arity = (int)(info.Type & InstType.ArityMask); string args = string.Empty; @@ -44,10 +47,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions VariableType dstType = GetSrcVarType(inst, argIndex); - args += GetSoureExpr(context, operation.GetSource(argIndex), dstType); + if (argIndex == 0 && atomic) + { + switch (inst & Instruction.MrMask) + { + // TODO: Global. + case Instruction.MrShared: args += LoadShared (context, operation); break; + case Instruction.MrStorage: args += LoadStorage(context, operation); break; + } + } + else + { + args += GetSoureExpr(context, operation.GetSource(argIndex), dstType); + } } - return info.OpName + "(" + args + ")"; + if (inst == Instruction.Ballot) + { + return $"unpackUint2x32({info.OpName}({args})).x"; + } + else + { + return info.OpName + "(" + args + ")"; + } } else if ((info.Type & InstType.Op) != 0) { @@ -99,6 +121,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.LoadLocal: return InstGenMemory.LoadLocal(context, operation); + case Instruction.LoadShared: + return InstGenMemory.LoadShared(context, operation); + case Instruction.LoadStorage: return InstGenMemory.LoadStorage(context, operation); @@ -108,6 +133,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions case Instruction.StoreLocal: return InstGenMemory.StoreLocal(context, operation); + case Instruction.StoreShared: + return InstGenMemory.StoreShared(context, operation); + case Instruction.StoreStorage: return InstGenMemory.StoreStorage(context, operation); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 2aaae71c..ec133272 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -13,8 +13,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { _infoTbl = new InstInfo[(int)Instruction.Count]; + Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomicAdd"); + Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomicAnd"); + Add(Instruction.AtomicCompareAndSwap, InstType.AtomicTernary, "atomicCompSwap"); + Add(Instruction.AtomicMaxS32, InstType.AtomicBinary, "atomicMax"); + Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "atomicMax"); + Add(Instruction.AtomicMinS32, InstType.AtomicBinary, "atomicMin"); + Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "atomicMin"); + Add(Instruction.AtomicOr, InstType.AtomicBinary, "atomicOr"); + Add(Instruction.AtomicSwap, InstType.AtomicBinary, "atomicExchange"); + Add(Instruction.AtomicXor, InstType.AtomicBinary, "atomicXor"); Add(Instruction.Absolute, InstType.CallUnary, "abs"); Add(Instruction.Add, InstType.OpBinaryCom, "+", 2); + Add(Instruction.Ballot, InstType.CallUnary, "ballotARB"); Add(Instruction.BitCount, InstType.CallUnary, "bitCount"); Add(Instruction.BitfieldExtractS32, InstType.CallTernary, "bitfieldExtract"); Add(Instruction.BitfieldExtractU32, InstType.CallTernary, "bitfieldExtract"); @@ -59,6 +70,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.LoadAttribute, InstType.Special); Add(Instruction.LoadConstant, InstType.Special); Add(Instruction.LoadLocal, InstType.Special); + Add(Instruction.LoadShared, InstType.Special); Add(Instruction.LoadStorage, InstType.Special); Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); @@ -87,6 +99,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.Sine, InstType.CallUnary, "sin"); Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); Add(Instruction.StoreLocal, InstType.Special); + Add(Instruction.StoreShared, InstType.Special); Add(Instruction.StoreStorage, InstType.Special); Add(Instruction.Subtract, InstType.OpBinary, "-", 2); Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); @@ -94,6 +107,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.TextureSize, InstType.Special); Add(Instruction.Truncate, InstType.CallUnary, "trunc"); Add(Instruction.UnpackHalf2x16, InstType.Special); + Add(Instruction.VoteAll, InstType.CallUnary, "allInvocationsARB"); + Add(Instruction.VoteAllEqual, InstType.CallUnary, "allInvocationsEqualARB"); + Add(Instruction.VoteAny, InstType.CallUnary, "anyInvocationARB"); } private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 21e39fcf..c535d8fc 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation.Optimizations; using System; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; @@ -118,27 +119,76 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return OperandManager.GetConstantBufferName(src1, offsetExpr, context.Config.Stage); } + public static string LoadGlobal(CodeGenContext context, AstOperation operation) + { + IAstNode src1 = operation.GetSource(0); + IAstNode src2 = operation.GetSource(1); + + string addrLowExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); + string addrHighExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + + context.AppendLine($"{DefaultNames.GmemOffsetName} = {HelperFunctionNames.GetStorageBuffer}({addrLowExpr}, {addrHighExpr});"); + + return GetStorageBufferAccessor($"{DefaultNames.GmemOffsetName}.x", $"{DefaultNames.GmemOffsetName}.y", context.Config.Stage); + } + public static string LoadLocal(CodeGenContext context, AstOperation operation) { + return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName); + } + + public static string LoadShared(CodeGenContext context, AstOperation operation) + { + return LoadLocalOrShared(context, operation, DefaultNames.SharedMemoryName); + } + + private static string LoadLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName) + { IAstNode src1 = operation.GetSource(0); string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - return $"{DefaultNames.LocalMemoryName}[{offsetExpr}]"; + return $"{arrayName}[{offsetExpr}]"; } public static string LoadStorage(CodeGenContext context, AstOperation operation) { IAstNode src1 = operation.GetSource(0); + + string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); + + return GetStorageBufferAccessor(operation.Index, offsetExpr, context.Config.Stage); + } + + public static string StoreGlobal(CodeGenContext context, AstOperation operation) + { + IAstNode src1 = operation.GetSource(0); IAstNode src2 = operation.GetSource(1); + IAstNode src3 = operation.GetSource(2); - string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + string addrLowExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); + string addrHighExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + string valueExpr = GetSoureExpr(context, src3, GetSrcVarType(operation.Inst, 2)); - return OperandManager.GetStorageBufferName(src1, offsetExpr, context.Config.Stage); + context.AppendLine($"{DefaultNames.GmemOffsetName} = {HelperFunctionNames.GetStorageBuffer}({addrLowExpr}, {addrHighExpr});"); + + string sb = GetStorageBufferAccessor($"{DefaultNames.GmemOffsetName}.x", $"{DefaultNames.GmemOffsetName}.y", context.Config.Stage); + + return $"{sb} = {valueExpr}"; } public static string StoreLocal(CodeGenContext context, AstOperation operation) { + return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName); + } + + public static string StoreShared(CodeGenContext context, AstOperation operation) + { + return StoreLocalOrShared(context, operation, DefaultNames.SharedMemoryName); + } + + private static string StoreLocalOrShared(CodeGenContext context, AstOperation operation, string arrayName) + { IAstNode src1 = operation.GetSource(0); IAstNode src2 = operation.GetSource(1); @@ -146,26 +196,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions VariableType srcType = OperandManager.GetNodeDestType(src2); - string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.F32); + string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32); - return $"{DefaultNames.LocalMemoryName}[{offsetExpr}] = {src}"; + return $"{arrayName}[{offsetExpr}] = {src}"; } public static string StoreStorage(CodeGenContext context, AstOperation operation) { IAstNode src1 = operation.GetSource(0); IAstNode src2 = operation.GetSource(1); - IAstNode src3 = operation.GetSource(2); - string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - VariableType srcType = OperandManager.GetNodeDestType(src3); + VariableType srcType = OperandManager.GetNodeDestType(src2); - string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.F32); + string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.U32); - string sbName = OperandManager.GetStorageBufferName(src1, offsetExpr, context.Config.Stage); + string sb = GetStorageBufferAccessor(operation.Index, offsetExpr, context.Config.Stage); - return $"{sbName} = {src}"; + return $"{sb} = {src}"; } public static string TextureSample(CodeGenContext context, AstOperation operation) @@ -402,7 +451,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Append(Src(VariableType.S32)); } - texCall += ")" + (isGather || !isShadow ? GetMask(texOp.ComponentMask) : ""); + texCall += ")" + (isGather || !isShadow ? GetMask(texOp.Index) : ""); return texCall; } @@ -428,22 +477,42 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0)); - return $"textureSize({samplerName}, {src0Expr}){GetMask(texOp.ComponentMask)}"; + return $"textureSize({samplerName}, {src0Expr}){GetMask(texOp.Index)}"; } - private static string GetMask(int compMask) + private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage) { - string mask = "."; + string sbName = OperandManager.GetShaderStagePrefix(stage); - for (int index = 0; index < 4; index++) - { - if ((compMask & (1 << index)) != 0) - { - mask += "rgba".Substring(index, 1); - } - } + sbName += "_" + DefaultNames.StorageNamePrefix; + + return $"{sbName}[{slotExpr}].{DefaultNames.DataName}[{offsetExpr}]"; + } + + private static string GetStorageBufferAccessor(int slot, string offsetExpr, ShaderStage stage) + { + string sbName = OperandManager.GetShaderStagePrefix(stage); + + sbName += "_" + DefaultNames.StorageNamePrefix; + + string mask = NumberFormatter.FormatUint(~(64u - 1)); - return mask; + // Subtract the base address of the global memory, to get the + // storage buffer offset. The mask is used to keep the lower bits, + // since the bound storage buffer must match the host alignment + // restrictions. + int ubOffset = GlobalToStorage.GetStorageCbOffset(stage, slot); + + string ubName = OperandManager.GetConstantBufferName(0, ubOffset, stage); + + offsetExpr = $"{offsetExpr} - int((floatBitsToUint({ubName}) & {mask}) >> 2)"; + + return $"{sbName}[{NumberFormatter.FormatInt(slot)}].{DefaultNames.DataName}[{offsetExpr}]"; + } + + private static string GetMask(int index) + { + return '.' + "rgba".Substring(index, 1); } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs index 4a40032c..e5167f93 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs @@ -24,22 +24,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string srcExpr = GetSoureExpr(context, src, GetSrcVarType(operation.Inst, 0)); - return $"unpackHalf2x16({srcExpr}){GetMask(operation.ComponentMask)}"; + return $"unpackHalf2x16({srcExpr}){GetMask(operation.Index)}"; } - private static string GetMask(int compMask) + private static string GetMask(int index) { - string mask = "."; - - for (int index = 0; index < 2; index++) - { - if ((compMask & (1 << index)) != 0) - { - mask += "xy".Substring(index, 1); - } - } - - return mask; + return '.' + "xy".Substring(index, 1); } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs index 121cd079..5836e981 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs @@ -8,8 +8,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions OpNullary = Op | 0, OpUnary = Op | 1, OpBinary = Op | 2, + OpBinaryCom = Op | 2 | Commutative, OpTernary = Op | 3, - OpBinaryCom = OpBinary | Commutative, + + AtomicBinary = CallBinary | Atomic, + AtomicTernary = CallTernary | Atomic, CallNullary = Call | 0, CallUnary = Call | 1, @@ -20,7 +23,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Commutative = 1 << 8, Op = 1 << 9, Call = 1 << 10, - Special = 1 << 11, + Atomic = 1 << 11, + Special = 1 << 12, ArityMask = 0xff } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 36f76ec5..4c9d5b55 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -51,13 +51,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, // Special. - { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) }, - { AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", VariableType.U32) }, - { AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", VariableType.U32) }, - { AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", VariableType.U32) }, - { AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", VariableType.U32) }, - { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) }, - { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) }, + { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) }, + { AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", VariableType.U32) }, + { AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", VariableType.U32) }, + { AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", VariableType.U32) }, + { AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", VariableType.U32) }, + { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) }, + { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) }, + { AttributeConsts.LaneId, new BuiltInAttribute("gl_SubGroupInvocationARB", VariableType.U32) }, + { AttributeConsts.EqMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupEqMaskARB).x", VariableType.U32) }, + { AttributeConsts.GeMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupGeMaskARB).x", VariableType.U32) }, + { AttributeConsts.GtMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupGtMaskARB).x", VariableType.U32) }, + { AttributeConsts.LeMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupLeMaskARB).x", VariableType.U32) }, + { AttributeConsts.LtMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupLtMaskARB).x", VariableType.U32) }, }; private Dictionary<AstOperand, string> _locals; @@ -87,7 +93,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return NumberFormatter.FormatInt(operand.Value); case OperandType.ConstantBuffer: - return GetConstantBufferName(operand, stage); + return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, stage); case OperandType.LocalVariable: return _locals[operand]; @@ -99,25 +105,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."); } - public static string GetConstantBufferName(AstOperand cbuf, ShaderStage stage) + public static string GetConstantBufferName(int slot, int offset, ShaderStage stage) { - string ubName = GetUbName(stage, cbuf.CbufSlot); + string ubName = GetUbName(stage, slot); - ubName += "[" + (cbuf.CbufOffset >> 2) + "]"; + ubName += "[" + (offset >> 2) + "]"; - return ubName + "." + GetSwizzleMask(cbuf.CbufOffset & 3); - } - - public static string GetStorageBufferName(IAstNode slot, string offsetExpr, ShaderStage stage) - { - // Non-constant slots are not supported. - // It is expected that upstream stages are never going to generate non-constant - // slot access. - AstOperand operand = (AstOperand)slot; - - string sbName = GetSbName(stage, operand.Value); - - return $"{sbName}[{offsetExpr}]"; + return ubName + "." + GetSwizzleMask(offset & 3); } public static string GetConstantBufferName(IAstNode slot, string offsetExpr, ShaderStage stage) @@ -205,15 +199,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0"; } - public static string GetSbName(ShaderStage stage, int slot) - { - string sbName = GetShaderStagePrefix(stage); - - sbName += "_" + DefaultNames.StorageNamePrefix + slot; - - return sbName + "_" + DefaultNames.StorageNameSuffix; - } - public static string GetUbName(ShaderStage stage, int slot) { string ubName = GetShaderStagePrefix(stage); diff --git a/Ryujinx.Graphics.Shader/Decoders/AtomicOp.cs b/Ryujinx.Graphics.Shader/Decoders/AtomicOp.cs new file mode 100644 index 00000000..065a57c4 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/AtomicOp.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum AtomicOp + { + Add = 0, + Minimum = 1, + Maximum = 2, + Increment = 3, + Decrement = 4, + BitwiseAnd = 5, + BitwiseOr = 6, + BitwiseExclusiveOr = 7, + Swap = 8 + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs index d840d49d..6d1382a8 100644 --- a/Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs +++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs @@ -1,10 +1,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { - interface IOpCodeAlu : IOpCodeRd, IOpCodeRa + interface IOpCodeAlu : IOpCodeRd, IOpCodeRa, IOpCodePredicate39 { - Register Predicate39 { get; } - - bool InvertP { get; } bool Extended { get; } bool SetCondCode { get; } bool Saturate { get; } diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodePredicate39.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodePredicate39.cs new file mode 100644 index 00000000..74e7aff1 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodePredicate39.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodePredicate39 + { + Register Predicate39 { get; } + + bool InvertP { get; } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAtom.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAtom.cs new file mode 100644 index 00000000..b572703e --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAtom.cs @@ -0,0 +1,39 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAtom : OpCode, IOpCodeRd, IOpCodeRa, IOpCodeReg + { + public Register Rd { get; } + public Register Ra { get; } + public Register Rb { get; } + + public ReductionType Type { get; } + + public int Offset { get; } + + public bool Extended { get; } + + public AtomicOp AtomicOp { get; } + + public OpCodeAtom(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); + + Type = (ReductionType)opCode.Extract(28, 2); + + if (Type == ReductionType.FP32FtzRn) + { + Type = ReductionType.S64; + } + + Offset = opCode.Extract(30, 22); + + Extended = opCode.Extract(48); + + AtomicOp = (AtomicOp)opCode.Extract(52, 4); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeRed.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeRed.cs new file mode 100644 index 00000000..8fde82a2 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeRed.cs @@ -0,0 +1,32 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeRed : OpCode, IOpCodeRd, IOpCodeRa + { + public Register Rd { get; } + public Register Ra { get; } + + public AtomicOp AtomicOp { get; } + + public ReductionType Type { get; } + + public int Offset { get; } + + public bool Extended { get; } + + public OpCodeRed(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + + Type = (ReductionType)opCode.Extract(20, 3); + + AtomicOp = (AtomicOp)opCode.Extract(23, 3); + + Offset = opCode.Extract(28, 20); + + Extended = opCode.Extract(48); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs index 7adaff61..58bd2b88 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs @@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Shader.Decoders #region Instructions Set("1110111111011x", InstEmit.Ald, typeof(OpCodeAttribute)); Set("1110111111110x", InstEmit.Ast, typeof(OpCodeAttribute)); + Set("11101100xxxxxx", InstEmit.Atoms, typeof(OpCodeAtom)); Set("0100110000000x", InstEmit.Bfe, typeof(OpCodeAluCbuf)); Set("0011100x00000x", InstEmit.Bfe, typeof(OpCodeAluImm)); Set("0101110000000x", InstEmit.Bfe, typeof(OpCodeAluReg)); @@ -122,6 +123,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("1110111101000x", InstEmit.Ld, typeof(OpCodeMemory)); Set("1110111110010x", InstEmit.Ldc, typeof(OpCodeLdc)); Set("1110111011010x", InstEmit.Ldg, typeof(OpCodeMemory)); + Set("1110111101001x", InstEmit.Lds, typeof(OpCodeMemory)); Set("0100110001000x", InstEmit.Lop, typeof(OpCodeLopCbuf)); Set("0011100001000x", InstEmit.Lop, typeof(OpCodeLopImm)); Set("000001xxxxxxxx", InstEmit.Lop, typeof(OpCodeLopImm32)); @@ -136,7 +138,11 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("0101000010000x", InstEmit.Mufu, typeof(OpCodeFArith)); Set("1111101111100x", InstEmit.Out, typeof(OpCode)); Set("111000101010xx", InstEmit.Pbk, typeof(OpCodeSsy)); + Set("0100110000001x", InstEmit.Popc, typeof(OpCodeAluCbuf)); + Set("0011100x00001x", InstEmit.Popc, typeof(OpCodeAluImm)); + Set("0101110000001x", InstEmit.Popc, typeof(OpCodeAluReg)); Set("0101000010010x", InstEmit.Psetp, typeof(OpCodePsetp)); + Set("1110101111111x", InstEmit.Red, typeof(OpCodeRed)); Set("0100110010010x", InstEmit.Rro, typeof(OpCodeFArithCbuf)); Set("0011100x10010x", InstEmit.Rro, typeof(OpCodeFArithImm)); Set("0101110010010x", InstEmit.Rro, typeof(OpCodeFArithReg)); @@ -154,6 +160,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy)); Set("1110111101010x", InstEmit.St, typeof(OpCodeMemory)); Set("1110111011011x", InstEmit.Stg, typeof(OpCodeMemory)); + Set("1110111101011x", InstEmit.Sts, typeof(OpCodeMemory)); Set("11101011001xxx", InstEmit.Sust, typeof(OpCodeImage)); Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync)); Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex)); @@ -168,6 +175,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex)); Set("1101111101010x", InstEmit.TxqB, typeof(OpCodeTex)); Set("01011111xxxxxx", InstEmit.Vmad, typeof(OpCodeVideo)); + Set("0101000011011x", InstEmit.Vote, typeof(OpCodeVote)); Set("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf)); Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm)); Set("010100010xxxxx", InstEmit.Xmad, typeof(OpCodeAluRegCbuf)); diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeVote.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeVote.cs new file mode 100644 index 00000000..374767bd --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeVote.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeVote : OpCode, IOpCodeRd, IOpCodePredicate39 + { + public Register Rd { get; } + public Register Predicate39 { get; } + public Register Predicate45 { get; } + + public VoteOp VoteOp { get; } + + public bool InvertP { get; } + + public OpCodeVote(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Predicate39 = new Register(opCode.Extract(39, 3), RegisterType.Predicate); + Predicate45 = new Register(opCode.Extract(45, 3), RegisterType.Predicate); + + InvertP = opCode.Extract(42); + + VoteOp = (VoteOp)opCode.Extract(48, 2); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/ReductionType.cs b/Ryujinx.Graphics.Shader/Decoders/ReductionType.cs new file mode 100644 index 00000000..aaa2186e --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/ReductionType.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum ReductionType + { + U32 = 0, + S32 = 1, + U64 = 2, + FP32FtzRn = 3, + U128 = 4, + S64 = 5 + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs b/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs index 2f3f4492..45ef3782 100644 --- a/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs +++ b/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs @@ -2,6 +2,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { enum SystemRegister { + LaneId = 0, YDirection = 0x12, ThreadId = 0x20, ThreadIdX = 0x21, @@ -9,6 +10,11 @@ namespace Ryujinx.Graphics.Shader.Decoders ThreadIdZ = 0x23, CtaIdX = 0x25, CtaIdY = 0x26, - CtaIdZ = 0x27 + CtaIdZ = 0x27, + EqMask = 0x38, + LtMask = 0x39, + LeMask = 0x3a, + GtMask = 0x3b, + GeMask = 0x3c } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Decoders/VoteOp.cs b/Ryujinx.Graphics.Shader/Decoders/VoteOp.cs new file mode 100644 index 00000000..2fe937c8 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/VoteOp.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum VoteOp + { + All = 0, + Any = 1, + AllEqual = 2 + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs index 8d14b0cf..31375e43 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs @@ -368,6 +368,19 @@ namespace Ryujinx.Graphics.Shader.Instructions SetZnFlags(context, dest, op.SetCondCode, op.Extended); } + public static void Popc(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool invert = op.RawOpCode.Extract(40); + + Operand srcB = context.BitwiseNot(GetSrcB(context), invert); + + Operand res = context.BitCount(srcB); + + context.Copy(GetDest(context), res); + } + public static void Psetp(EmitterContext context) { OpCodePsetp op = (OpCodePsetp)context.CurrOp; diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs index c0a3012a..ddacc151 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs @@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Shader.Instructions public static Operand GetPredicate39(EmitterContext context) { - IOpCodeAlu op = (IOpCodeAlu)context.CurrOp; + IOpCodePredicate39 op = (IOpCodePredicate39)context.CurrOp; Operand local = Register(op.Predicate39); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index ee210f22..d76f44cb 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -9,6 +9,13 @@ namespace Ryujinx.Graphics.Shader.Instructions { static partial class InstEmit { + private enum MemoryRegion + { + Global, + Local, + Shared + } + public static void Ald(EmitterContext context) { OpCodeAttribute op = (OpCodeAttribute)context.CurrOp; @@ -49,6 +56,21 @@ namespace Ryujinx.Graphics.Shader.Instructions } } + public static void Atoms(EmitterContext context) + { + OpCodeAtom op = (OpCodeAtom)context.CurrOp; + + Operand mem = context.ShiftRightU32(GetSrcA(context), Const(2)); + + mem = context.IAdd(mem, Const(op.Offset)); + + Operand value = GetSrcB(context); + + Operand res = EmitAtomicOp(context, Instruction.MrShared, op.AtomicOp, op.Type, mem, value); + + context.Copy(GetDest(context), res); + } + public static void Ipa(EmitterContext context) { OpCodeIpa op = (OpCodeIpa)context.CurrOp; @@ -80,7 +102,7 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Ld(EmitterContext context) { - LoadLocalOrGlobal(context, isGlobal: false); + EmitLoad(context, MemoryRegion.Local); } public static void Ldc(EmitterContext context) @@ -126,7 +148,12 @@ namespace Ryujinx.Graphics.Shader.Instructions public static void Ldg(EmitterContext context) { - LoadLocalOrGlobal(context, isGlobal: true); + EmitLoad(context, MemoryRegion.Global); + } + + public static void Lds(EmitterContext context) + { + EmitLoad(context, MemoryRegion.Shared); } public static void Out(EmitterContext context) @@ -152,17 +179,118 @@ namespace Ryujinx.Graphics.Shader.Instructions } } + public static void Red(EmitterContext context) + { + OpCodeRed op = (OpCodeRed)context.CurrOp; + + Operand offset = context.IAdd(GetSrcA(context), Const(op.Offset)); + + Operand mem = context.ShiftRightU32(offset, Const(2)); + + EmitAtomicOp(context, Instruction.MrGlobal, op.AtomicOp, op.Type, mem, GetDest(context)); + } + public static void St(EmitterContext context) { - StoreLocalOrGlobal(context, isGlobal: false); + EmitStore(context, MemoryRegion.Local); } public static void Stg(EmitterContext context) { - StoreLocalOrGlobal(context, isGlobal: true); + EmitStore(context, MemoryRegion.Global); + } + + public static void Sts(EmitterContext context) + { + EmitStore(context, MemoryRegion.Shared); } - private static void LoadLocalOrGlobal(EmitterContext context, bool isGlobal) + private static Operand EmitAtomicOp( + EmitterContext context, + Instruction mr, + AtomicOp op, + ReductionType type, + Operand mem, + Operand value) + { + Operand res = null; + + switch (op) + { + case AtomicOp.Add: + if (type == ReductionType.S32 || type == ReductionType.U32) + { + res = context.AtomicAdd(mr, mem, value); + } + else + { + // Not supported or invalid. + } + break; + case AtomicOp.BitwiseAnd: + if (type == ReductionType.S32 || type == ReductionType.U32) + { + res = context.AtomicAnd(mr, mem, value); + } + else + { + // Not supported or invalid. + } + break; + case AtomicOp.BitwiseExclusiveOr: + if (type == ReductionType.S32 || type == ReductionType.U32) + { + res = context.AtomicXor(mr, mem, value); + } + else + { + // Not supported or invalid. + } + break; + case AtomicOp.BitwiseOr: + if (type == ReductionType.S32 || type == ReductionType.U32) + { + res = context.AtomicOr(mr, mem, value); + } + else + { + // Not supported or invalid. + } + break; + case AtomicOp.Maximum: + if (type == ReductionType.S32) + { + res = context.AtomicMaxS32(mr, mem, value); + } + else if (type == ReductionType.U32) + { + res = context.AtomicMaxU32(mr, mem, value); + } + else + { + // Not supported or invalid. + } + break; + case AtomicOp.Minimum: + if (type == ReductionType.S32) + { + res = context.AtomicMinS32(mr, mem, value); + } + else if (type == ReductionType.U32) + { + res = context.AtomicMinU32(mr, mem, value); + } + else + { + // Not supported or invalid. + } + break; + } + + return res; + } + + private static void EmitLoad(EmitterContext context, MemoryRegion region) { OpCodeMemory op = (OpCodeMemory)context.CurrOp; @@ -199,9 +327,14 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand offset = context.IAdd(wordOffset, Const(index)); - Operand value = isGlobal - ? context.LoadGlobal(offset) - : context.LoadLocal (offset); + Operand value = null; + + switch (region) + { + case MemoryRegion.Global: value = context.LoadGlobal(offset); break; + case MemoryRegion.Local: value = context.LoadLocal (offset); break; + case MemoryRegion.Shared: value = context.LoadShared(offset); break; + } if (isSmallInt) { @@ -212,7 +345,7 @@ namespace Ryujinx.Graphics.Shader.Instructions } } - private static void StoreLocalOrGlobal(EmitterContext context, bool isGlobal) + private static void EmitStore(EmitterContext context, MemoryRegion region) { OpCodeMemory op = (OpCodeMemory)context.CurrOp; @@ -241,31 +374,34 @@ namespace Ryujinx.Graphics.Shader.Instructions { Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); - if (rd.IsRZ) - { - break; - } - Operand value = Register(rd); Operand offset = context.IAdd(wordOffset, Const(index)); if (isSmallInt) { - Operand word = isGlobal - ? context.LoadGlobal(offset) - : context.LoadLocal (offset); + Operand word = null; + + switch (region) + { + case MemoryRegion.Global: word = context.LoadGlobal(offset); break; + case MemoryRegion.Local: word = context.LoadLocal (offset); break; + case MemoryRegion.Shared: word = context.LoadShared(offset); break; + } value = InsertSmallInt(context, op.Size, bitOffset, word, value); } - if (isGlobal) + switch (region) { - context.StoreGlobal(offset, value); + case MemoryRegion.Global: context.StoreGlobal(offset, value); break; + case MemoryRegion.Local: context.StoreLocal (offset, value); break; + case MemoryRegion.Shared: context.StoreShared(offset, value); break; } - else + + if (rd.IsRZ) { - context.StoreLocal(offset, value); + break; } } } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index f0792245..5833d879 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -27,6 +27,8 @@ namespace Ryujinx.Graphics.Shader.Instructions switch (sysReg) { + case SystemRegister.LaneId: src = Attribute(AttributeConsts.LaneId); break; + // TODO: Use value from Y direction GPU register. case SystemRegister.YDirection: src = ConstF(1); break; @@ -50,6 +52,11 @@ namespace Ryujinx.Graphics.Shader.Instructions case SystemRegister.CtaIdX: src = Attribute(AttributeConsts.CtaIdX); break; case SystemRegister.CtaIdY: src = Attribute(AttributeConsts.CtaIdY); break; case SystemRegister.CtaIdZ: src = Attribute(AttributeConsts.CtaIdZ); break; + case SystemRegister.EqMask: src = Attribute(AttributeConsts.EqMask); break; + case SystemRegister.LtMask: src = Attribute(AttributeConsts.LtMask); break; + case SystemRegister.LeMask: src = Attribute(AttributeConsts.LeMask); break; + case SystemRegister.GtMask: src = Attribute(AttributeConsts.GtMask); break; + case SystemRegister.GeMask: src = Attribute(AttributeConsts.GeMask); break; default: src = Const(0); break; } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs new file mode 100644 index 00000000..9c4d5f1a --- /dev/null +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs @@ -0,0 +1,48 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +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 Vote(EmitterContext context) + { + OpCodeVote op = (OpCodeVote)context.CurrOp; + + Operand pred = GetPredicate39(context); + + Operand res = null; + + switch (op.VoteOp) + { + case VoteOp.All: + res = context.VoteAll(pred); + break; + case VoteOp.Any: + res = context.VoteAny(pred); + break; + case VoteOp.AllEqual: + res = context.VoteAllEqual(pred); + break; + } + + if (res != null) + { + context.Copy(Register(op.Predicate45), res); + } + else + { + // Invalid. + } + + if (!op.Rd.IsRZ) + { + context.Copy(Register(op.Rd), context.Ballot(pred)); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index 46c6b57f..d99e3f2b 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -7,6 +7,17 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { Absolute = 1, Add, + AtomicAdd, + AtomicAnd, + AtomicCompareAndSwap, + AtomicMinS32, + AtomicMinU32, + AtomicMaxS32, + AtomicMaxU32, + AtomicOr, + AtomicSwap, + AtomicXor, + Ballot, BitCount, BitfieldExtractS32, BitfieldExtractU32, @@ -57,6 +68,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation LoadConstant, LoadGlobal, LoadLocal, + LoadShared, LoadStorage, LogarithmB2, LogicalAnd, @@ -88,6 +100,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation SquareRoot, StoreGlobal, StoreLocal, + StoreShared, StoreStorage, Subtract, SwizzleAdd, @@ -96,9 +109,44 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Truncate, UnpackDouble2x32, UnpackHalf2x16, + VoteAll, + VoteAllEqual, + VoteAny, Count, - FP = 1 << 16, + + FP = 1 << 16, + + MrShift = 17, + + MrGlobal = 0 << MrShift, + MrShared = 1 << MrShift, + MrStorage = 2 << MrShift, + MrMask = 3 << MrShift, + Mask = 0xffff } + + static class InstructionExtensions + { + public static bool IsAtomic(this Instruction inst) + { + switch (inst & Instruction.Mask) + { + case Instruction.AtomicAdd: + case Instruction.AtomicAnd: + case Instruction.AtomicCompareAndSwap: + case Instruction.AtomicMaxS32: + case Instruction.AtomicMaxU32: + case Instruction.AtomicMinS32: + case Instruction.AtomicMinU32: + case Instruction.AtomicOr: + case Instruction.AtomicSwap: + case Instruction.AtomicXor: + return true; + } + + return false; + } + } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index 0d7379a8..6b7fb82f 100644 --- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public int SourcesCount => _sources.Length; - public int ComponentIndex { get; } + public int Index { get; } public Operation(Instruction inst, Operand dest, params Operand[] sources) { @@ -39,11 +39,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public Operation( Instruction inst, - int compIndex, + int index, Operand dest, params Operand[] sources) : this(inst, dest, sources) { - ComponentIndex = compIndex; + Index = index; } private Operand AssignDest(Operand dest) diff --git a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index e10d1eda..a046c2f9 100644 --- a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -1,6 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> + <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\GlobalMemory.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleDown.glsl" /> <EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" /> diff --git a/Ryujinx.Graphics.Shader/ShaderProgram.cs b/Ryujinx.Graphics.Shader/ShaderProgram.cs index 52c2d55b..5d04f2cf 100644 --- a/Ryujinx.Graphics.Shader/ShaderProgram.cs +++ b/Ryujinx.Graphics.Shader/ShaderProgram.cs @@ -1,3 +1,5 @@ +using System; + namespace Ryujinx.Graphics.Shader { public class ShaderProgram @@ -15,6 +17,11 @@ namespace Ryujinx.Graphics.Shader Code = code; } + public void Prepend(string line) + { + Code = line + Environment.NewLine + Code; + } + public void Replace(string name, string value) { Code = Code.Replace(name, value); diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs index 1607ffec..76eee71e 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { public Instruction Inst { get; } - public int ComponentMask { get; } + public int Index { get; } private IAstNode[] _sources; @@ -24,12 +24,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AddUse(source, this); } - ComponentMask = 1; + Index = 0; } - public AstOperation(Instruction inst, int compMask, params IAstNode[] sources) : this(inst, sources) + public AstOperation(Instruction inst, int index, params IAstNode[] sources) : this(inst, sources) { - ComponentMask = compMask; + Index = index; } public IAstNode GetSource(int index) diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index d6d40732..5473978e 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -16,8 +16,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr TextureFlags flags, int handle, int arraySize, - int compMask, - params IAstNode[] sources) : base(inst, compMask, sources) + int index, + params IAstNode[] sources) : base(inst, index, sources) { Type = type; Flags = flags; diff --git a/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs index e2eee78d..b262e6bc 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs @@ -5,10 +5,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr [Flags] enum HelperFunctionsMask { - Shuffle = 1 << 0, - ShuffleDown = 1 << 1, - ShuffleUp = 1 << 2, - ShuffleXor = 1 << 3, - SwizzleAdd = 1 << 4 + GlobalMemory = 1 << 0, + Shuffle = 1 << 1, + ShuffleDown = 1 << 2, + ShuffleUp = 1 << 3, + ShuffleXor = 1 << 4, + SwizzleAdd = 1 << 5 } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index 381cf292..4f4ebb99 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -25,8 +25,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr _infoTbl = new InstInfo[(int)Instruction.Count]; // Inst Destination type Source 1 type Source 2 type Source 3 type Source 4 type + Add(Instruction.AtomicAdd, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.AtomicAnd, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.AtomicCompareAndSwap, VariableType.U32, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.AtomicMaxS32, VariableType.S32, VariableType.S32, VariableType.S32); + Add(Instruction.AtomicMaxU32, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.AtomicMinS32, VariableType.S32, VariableType.S32, VariableType.S32); + Add(Instruction.AtomicMinU32, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.AtomicOr, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.AtomicSwap, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.AtomicXor, VariableType.U32, VariableType.U32, VariableType.U32); Add(Instruction.Absolute, VariableType.Scalar, VariableType.Scalar); Add(Instruction.Add, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.Ballot, VariableType.U32, VariableType.Bool); Add(Instruction.BitCount, VariableType.Int, VariableType.Int); Add(Instruction.BitfieldExtractS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32); Add(Instruction.BitfieldExtractU32, VariableType.U32, VariableType.U32, VariableType.S32, VariableType.S32); @@ -69,9 +80,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.IsNan, VariableType.Bool, VariableType.F32); Add(Instruction.LoadAttribute, VariableType.F32, VariableType.S32, VariableType.S32); Add(Instruction.LoadConstant, VariableType.F32, VariableType.S32, VariableType.S32); - Add(Instruction.LoadGlobal, VariableType.F32, VariableType.S32, VariableType.S32); + Add(Instruction.LoadGlobal, VariableType.U32, VariableType.S32, VariableType.S32); Add(Instruction.LoadLocal, VariableType.F32, VariableType.S32); - Add(Instruction.LoadStorage, VariableType.F32, VariableType.S32, VariableType.S32); + Add(Instruction.LoadShared, VariableType.U32, VariableType.S32); + Add(Instruction.LoadStorage, VariableType.U32, VariableType.S32); Add(Instruction.LogarithmB2, VariableType.Scalar, VariableType.Scalar); Add(Instruction.LogicalAnd, VariableType.Bool, VariableType.Bool, VariableType.Bool); Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool); @@ -95,15 +107,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.Round, VariableType.F32, VariableType.F32); Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar); Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar); - Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.F32); + Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32); Add(Instruction.StoreLocal, VariableType.None, VariableType.S32, VariableType.F32); - Add(Instruction.StoreStorage, VariableType.None, VariableType.S32, VariableType.S32, VariableType.F32); + Add(Instruction.StoreShared, VariableType.None, VariableType.S32, VariableType.U32); + Add(Instruction.StoreStorage, VariableType.None, VariableType.S32, VariableType.U32); Add(Instruction.Subtract, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); Add(Instruction.SwizzleAdd, VariableType.F32, VariableType.F32, VariableType.F32, VariableType.S32); Add(Instruction.TextureSample, VariableType.F32); Add(Instruction.TextureSize, VariableType.S32, VariableType.S32, VariableType.S32); Add(Instruction.Truncate, VariableType.F32, VariableType.F32); Add(Instruction.UnpackHalf2x16, VariableType.F32, VariableType.U32); + Add(Instruction.VoteAll, VariableType.Bool, VariableType.Bool); + Add(Instruction.VoteAllEqual, VariableType.Bool, VariableType.Bool); + Add(Instruction.VoteAny, VariableType.Bool, VariableType.Bool); } private static void Add(Instruction inst, VariableType destType, params VariableType[] srcTypes) diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index ef8443ab..a81b3d12 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -51,8 +51,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr sources[index] = context.GetOperandUse(operation.GetSource(index)); } - int componentMask = 1 << operation.ComponentIndex; - AstTextureOperation GetAstTextureOperation(TextureOperation texOp) { return new AstTextureOperation( @@ -61,7 +59,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr texOp.Flags, texOp.Handle, 4, // TODO: Non-hardcoded array size. - componentMask, + texOp.Index, sources); } @@ -80,16 +78,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr context.Info.CBuffers.Add(slot.Value); } - else if (inst == Instruction.LoadStorage) + else if (UsesStorage(inst)) { - Operand slot = operation.GetSource(0); - - if (slot.Type != OperandType.Constant) - { - throw new InvalidOperationException("Found load or store with non-constant storage buffer slot."); - } - - context.Info.SBuffers.Add(slot.Value); + context.Info.SBuffers.Add(operation.Index); } AstAssignment assignment; @@ -141,7 +132,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else if (!isCopy) { - source = new AstOperation(inst, componentMask, sources); + source = new AstOperation(inst, operation.Index, sources); } else { @@ -166,19 +157,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } else { - if (inst == Instruction.StoreStorage) + if (UsesStorage(inst)) { - Operand slot = operation.GetSource(0); - - if (slot.Type != OperandType.Constant) - { - throw new InvalidOperationException("Found load or store with non-constant storage buffer slot."); - } - - context.Info.SBuffers.Add(slot.Value); + context.Info.SBuffers.Add(operation.Index); } - context.AddNode(new AstOperation(inst, sources)); + context.AddNode(new AstOperation(inst, operation.Index, sources)); } // Those instructions needs to be emulated by using helper functions, @@ -186,6 +170,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr // decide which helper functions are needed on the final generated code. switch (operation.Inst) { + case Instruction.LoadGlobal: + case Instruction.StoreGlobal: + context.Info.HelperFunctionsMask |= HelperFunctionsMask.GlobalMemory; + break; case Instruction.Shuffle: context.Info.HelperFunctionsMask |= HelperFunctionsMask.Shuffle; break; @@ -320,5 +308,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr throw new ArgumentException($"Unexpected instruction \"{inst}\"."); } + + private static bool UsesStorage(Instruction inst) + { + if (inst == Instruction.LoadStorage || inst == Instruction.StoreStorage) + { + return true; + } + + return inst.IsAtomic() && (inst & Instruction.MrMask) == Instruction.MrStorage; + } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 08aac1ca..8ff37429 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -42,5 +42,13 @@ namespace Ryujinx.Graphics.Shader.Translation public const int CtaIdX = 0x2000010; public const int CtaIdY = 0x2000014; public const int CtaIdZ = 0x2000018; + + public const int LaneId = 0x2000020; + + public const int EqMask = 0x2000024; + public const int GeMask = 0x2000028; + public const int GtMask = 0x200002c; + public const int LeMask = 0x2000030; + public const int LtMask = 0x2000034; } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index 58a37b52..e94d4d2d 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -6,6 +6,61 @@ namespace Ryujinx.Graphics.Shader.Translation { static class EmitterContextInsts { + public static Operand AtomicAdd(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicAdd | mr, Local(), a, b); + } + + public static Operand AtomicAnd(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicAnd | mr, Local(), a, b); + } + + public static Operand AtomicCompareAndSwap(this EmitterContext context, Instruction mr, Operand a, Operand b, Operand c) + { + return context.Add(Instruction.AtomicCompareAndSwap | mr, Local(), a, b, c); + } + + public static Operand AtomicMaxS32(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicMaxS32 | mr, Local(), a, b); + } + + public static Operand AtomicMaxU32(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicMaxU32 | mr, Local(), a, b); + } + + public static Operand AtomicMinS32(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicMinS32 | mr, Local(), a, b); + } + + public static Operand AtomicMinU32(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicMinU32 | mr, Local(), a, b); + } + + public static Operand AtomicOr(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicOr | mr, Local(), a, b); + } + + public static Operand AtomicSwap(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicSwap | mr, Local(), a, b); + } + + public static Operand AtomicXor(this EmitterContext context, Instruction mr, Operand a, Operand b) + { + return context.Add(Instruction.AtomicXor | mr, Local(), a, b); + } + + public static Operand Ballot(this EmitterContext context, Operand a) + { + return context.Add(Instruction.Ballot, Local(), a); + } + public static Operand BitCount(this EmitterContext context, Operand a) { return context.Add(Instruction.BitCount, Local(), a); @@ -411,6 +466,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.LoadLocal, Local(), a); } + public static Operand LoadShared(this EmitterContext context, Operand a) + { + return context.Add(Instruction.LoadShared, Local(), a); + } + public static Operand PackHalf2x16(this EmitterContext context, Operand a, Operand b) { return context.Add(Instruction.PackHalf2x16, Local(), a, b); @@ -468,6 +528,11 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.StoreLocal, null, a, b); } + public static Operand StoreShared(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.StoreShared, null, a, b); + } + public static Operand UnpackHalf2x16High(this EmitterContext context, Operand a) { return UnpackHalf2x16(context, a, 1); @@ -486,5 +551,20 @@ namespace Ryujinx.Graphics.Shader.Translation return dest; } + + public static Operand VoteAll(this EmitterContext context, Operand a) + { + return context.Add(Instruction.VoteAll, Local(), a); + } + + public static Operand VoteAllEqual(this EmitterContext context, Operand a) + { + return context.Add(Instruction.VoteAllEqual, Local(), a); + } + + public static Operand VoteAny(this EmitterContext context, Operand a) + { + return context.Add(Instruction.VoteAny, Local(), a); + } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs index 97852ac1..b6958929 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs @@ -256,7 +256,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { int value = operation.GetSource(0).Value; - value = (value >> operation.ComponentIndex * 16) & 0xffff; + value = (value >> operation.Index * 16) & 0xffff; operation.TurnIntoCopy(ConstF(HalfConversion.HalfToSingle(value))); } diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index 3d89faf6..2fafa5ad 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -1,8 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System.Collections.Generic; -using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; - namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class GlobalToStorage @@ -27,7 +25,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - if (operation.Inst == Instruction.LoadGlobal || + if (operation.Inst.IsAtomic() || + operation.Inst == Instruction.LoadGlobal || operation.Inst == Instruction.StoreGlobal) { Operand source = operation.GetSource(0); @@ -51,18 +50,31 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operation storageOp; - if (operation.Inst == Instruction.LoadGlobal) + if (operation.Inst.IsAtomic()) + { + Operand[] sources = new Operand[operation.SourcesCount]; + + for (int index = 0; index < operation.SourcesCount; index++) + { + sources[index] = operation.GetSource(index); + } + + Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage; + + storageOp = new Operation(inst, storageIndex, operation.Dest, sources); + } + else if (operation.Inst == Instruction.LoadGlobal) { Operand source = operation.GetSource(0); - storageOp = new Operation(Instruction.LoadStorage, operation.Dest, Const(storageIndex), source); + storageOp = new Operation(Instruction.LoadStorage, storageIndex, operation.Dest, source); } else { Operand src1 = operation.GetSource(0); Operand src2 = operation.GetSource(1); - storageOp = new Operation(Instruction.StoreStorage, null, Const(storageIndex), src1, src2); + storageOp = new Operation(Instruction.StoreStorage, storageIndex, null, src1, src2); } for (int index = 0; index < operation.SourcesCount; index++) @@ -114,6 +126,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return -1; } + public static int GetStorageCbOffset(ShaderStage stage, int slot) + { + return GetStorageBaseCbOffset(stage) + slot * StorageDescSize; + } + private static int GetStorageBaseCbOffset(ShaderStage stage) { switch (stage) diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index 6ee27884..93d86541 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (operation.GetSource(0) == dest) { - operation.TurnIntoCopy(operation.ComponentIndex == 1 ? src1 : src0); + operation.TurnIntoCopy(operation.Index == 1 ? src1 : src0); modified = true; } @@ -251,7 +251,30 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations private static bool IsUnused(INode node) { - return DestIsLocalVar(node) && node.Dest.UseOps.Count == 0; + return !HasSideEffects(node) && DestIsLocalVar(node) && node.Dest.UseOps.Count == 0; + } + + private static bool HasSideEffects(INode node) + { + if (node is Operation operation) + { + switch (operation.Inst & Instruction.Mask) + { + case Instruction.AtomicAdd: + case Instruction.AtomicAnd: + case Instruction.AtomicCompareAndSwap: + case Instruction.AtomicMaxS32: + case Instruction.AtomicMaxU32: + case Instruction.AtomicMinS32: + case Instruction.AtomicMinU32: + case Instruction.AtomicOr: + case Instruction.AtomicSwap: + case Instruction.AtomicXor: + return true; + } + } + + return false; } private static bool DestIsLocalVar(INode node) diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index aaf618e9..b7a5bffa 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -15,25 +15,38 @@ namespace Ryujinx.Graphics.Shader.Translation { private const int HeaderSize = 0x50; - public static ShaderProgram Translate(Span<byte> code, TranslationConfig translationConfig) + public static Span<byte> ExtractCode(Span<byte> code, bool compute, out int headerSize) { - return Translate(code, Span<byte>.Empty, translationConfig); + if (compute) + { + headerSize = 0; + } + else + { + headerSize = HeaderSize; + } + + Block[] cfg = Decoder.Decode(code, (ulong)headerSize); + + ulong endAddress = 0; + + foreach (Block block in cfg) + { + if (endAddress < block.EndAddress) + { + endAddress = block.EndAddress; + } + } + + return code.Slice(0, headerSize + (int)endAddress); } - public static ShaderProgram Translate(Span<byte> code, Span<byte> code2, TranslationConfig translationConfig) + public static ShaderProgram Translate(Span<byte> code, TranslationConfig translationConfig) { bool compute = (translationConfig.Flags & TranslationFlags.Compute) != 0; bool debugMode = (translationConfig.Flags & TranslationFlags.DebugMode) != 0; - Operation[] shaderOps = DecodeShader(code, compute, debugMode, out ShaderHeader header); - - if (code2 != Span<byte>.Empty) - { - // Dual vertex shader. - Operation[] shaderOpsB = DecodeShader(code2, compute, debugMode, out header); - - shaderOps = Combine(shaderOps, shaderOpsB); - } + Operation[] ops = DecodeShader(code, compute, debugMode, out ShaderHeader header); ShaderStage stage; @@ -63,7 +76,29 @@ namespace Ryujinx.Graphics.Shader.Translation maxOutputVertexCount, outputTopology); - BasicBlock[] irBlocks = ControlFlowGraph.MakeCfg(shaderOps); + return Translate(ops, config); + } + + public static ShaderProgram Translate(Span<byte> vpACode, Span<byte> vpBCode, TranslationConfig translationConfig) + { + bool debugMode = (translationConfig.Flags & TranslationFlags.DebugMode) != 0; + + Operation[] vpAOps = DecodeShader(vpACode, compute: false, debugMode, out _); + Operation[] vpBOps = DecodeShader(vpBCode, compute: false, debugMode, out ShaderHeader header); + + ShaderConfig config = new ShaderConfig( + header.Stage, + translationConfig.Flags, + translationConfig.MaxCBufferSize, + header.MaxOutputVertexCount, + header.OutputTopology); + + return Translate(Combine(vpAOps, vpBOps), config); + } + + private static ShaderProgram Translate(Operation[] ops, ShaderConfig config) + { + BasicBlock[] irBlocks = ControlFlowGraph.MakeCfg(ops); Dominance.FindDominators(irBlocks[0], irBlocks.Length); @@ -71,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.Translation Ssa.Rename(irBlocks); - Optimizer.Optimize(irBlocks, stage); + Optimizer.Optimize(irBlocks, config.Stage); StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(irBlocks, config); @@ -87,12 +122,7 @@ namespace Ryujinx.Graphics.Shader.Translation string glslCode = program.Code; - if (translationConfig.Version != 0) - { - glslCode = "// " + translationConfig.Version + Environment.NewLine + glslCode; - } - - return new ShaderProgram(spInfo, stage, glslCode); + return new ShaderProgram(spInfo, config.Stage, glslCode); } private static Operation[] DecodeShader(Span<byte> code, bool compute, bool debugMode, out ShaderHeader header) |
