diff options
author | gdkchan <gab.dark.100@gmail.com> | 2020-10-25 17:00:44 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-25 17:00:44 -0300 |
commit | 49f970d5bd9163e2b4e26a33ef8f84529174d5de (patch) | |
tree | eceaf9c0454d27413ca77689c06a24b47467d1a0 /Ryujinx.Graphics.Shader/Translation/Translator.cs | |
parent | 973a615d405a83d5fc2f6a11ad12ba63c2a76465 (diff) |
Implement CAL and RET shader instructions (#1618)
* Add support for CAL and RET shader instructions
* Remove unused stuff
* Fix a bug that could cause the wrong values to be passed to a function
* Avoid repopulating function id dictionary every time
* PR feedback
* Fix vertex shader A/B merge
Diffstat (limited to 'Ryujinx.Graphics.Shader/Translation/Translator.cs')
-rw-r--r-- | Ryujinx.Graphics.Shader/Translation/Translator.cs | 260 |
1 files changed, 168 insertions, 92 deletions
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index db0924b3..f8093c84 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -14,6 +14,16 @@ namespace Ryujinx.Graphics.Shader.Translation { private const int HeaderSize = 0x50; + private struct FunctionCode + { + public Operation[] Code { get; } + + public FunctionCode(Operation[] code) + { + Code = code; + } + } + public static ShaderProgram Translate(ulong address, IGpuAccessor gpuAccessor, TranslationFlags flags) { return Translate(DecodeShader(address, gpuAccessor, flags, out ShaderConfig config), config); @@ -21,32 +31,65 @@ namespace Ryujinx.Graphics.Shader.Translation public static ShaderProgram Translate(ulong addressA, ulong addressB, IGpuAccessor gpuAccessor, TranslationFlags flags) { - Operation[] opsA = DecodeShader(addressA, gpuAccessor, flags | TranslationFlags.VertexA, out ShaderConfig configA); - Operation[] opsB = DecodeShader(addressB, gpuAccessor, flags, out ShaderConfig config); + FunctionCode[] funcA = DecodeShader(addressA, gpuAccessor, flags | TranslationFlags.VertexA, out ShaderConfig configA); + FunctionCode[] funcB = DecodeShader(addressB, gpuAccessor, flags, out ShaderConfig config); config.SetUsedFeature(configA.UsedFeatures); - return Translate(Combine(opsA, opsB), config, configA.Size); + return Translate(Combine(funcA, funcB), config, configA.Size); } - private static ShaderProgram Translate(Operation[] ops, ShaderConfig config, int sizeA = 0) + private static ShaderProgram Translate(FunctionCode[] functions, ShaderConfig config, int sizeA = 0) { - BasicBlock[] blocks = ControlFlowGraph.MakeCfg(ops); + var cfgs = new ControlFlowGraph[functions.Length]; + var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; - if (blocks.Length > 0) + for (int i = 0; i < functions.Length; i++) { - Dominance.FindDominators(blocks[0], blocks.Length); + cfgs[i] = ControlFlowGraph.Create(functions[i].Code); + + if (i != 0) + { + frus[i] = RegisterUsage.RunPass(cfgs[i]); + } + } + + Function[] funcs = new Function[functions.Length]; + + for (int i = 0; i < functions.Length; i++) + { + var cfg = cfgs[i]; + + int inArgumentsCount = 0; + int outArgumentsCount = 0; + + if (i != 0) + { + var fru = frus[i]; + + inArgumentsCount = fru.InArguments.Length; + outArgumentsCount = fru.OutArguments.Length; + } + + if (cfg.Blocks.Length != 0) + { + RegisterUsage.FixupCalls(cfg.Blocks, frus); + + Dominance.FindDominators(cfg); - Dominance.FindDominanceFrontiers(blocks); + Dominance.FindDominanceFrontiers(cfg.Blocks); - Ssa.Rename(blocks); + Ssa.Rename(cfg.Blocks); - Optimizer.RunPass(blocks, config); + Optimizer.RunPass(cfg.Blocks, config); - Lowering.RunPass(blocks, config); + Lowering.RunPass(cfg.Blocks, config); + } + + funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); } - StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(blocks, config); + StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(funcs, config); GlslProgram program = GlslGenerator.Generate(sInfo, config); @@ -62,9 +105,9 @@ namespace Ryujinx.Graphics.Shader.Translation return new ShaderProgram(spInfo, config.Stage, glslCode, config.Size, sizeA); } - private static Operation[] DecodeShader(ulong address, IGpuAccessor gpuAccessor, TranslationFlags flags, out ShaderConfig config) + private static FunctionCode[] DecodeShader(ulong address, IGpuAccessor gpuAccessor, TranslationFlags flags, out ShaderConfig config) { - Block[] cfg; + Block[][] cfg; if ((flags & TranslationFlags.Compute) != 0) { @@ -83,112 +126,131 @@ namespace Ryujinx.Graphics.Shader.Translation { gpuAccessor.Log("Invalid branch detected, failed to build CFG."); - return Array.Empty<Operation>(); + return Array.Empty<FunctionCode>(); + } + + Dictionary<ulong, int> funcIds = new Dictionary<ulong, int>(); + + for (int funcIndex = 0; funcIndex < cfg.Length; funcIndex++) + { + funcIds.Add(cfg[funcIndex][0].Address, funcIndex); } - EmitterContext context = new EmitterContext(config); + List<FunctionCode> funcs = new List<FunctionCode>(); ulong maxEndAddress = 0; - for (int blkIndex = 0; blkIndex < cfg.Length; blkIndex++) + for (int funcIndex = 0; funcIndex < cfg.Length; funcIndex++) { - Block block = cfg[blkIndex]; + EmitterContext context = new EmitterContext(config, funcIndex != 0, funcIds); - if (maxEndAddress < block.EndAddress) + for (int blkIndex = 0; blkIndex < cfg[funcIndex].Length; blkIndex++) { - maxEndAddress = block.EndAddress; - } + Block block = cfg[funcIndex][blkIndex]; - context.CurrBlock = block; + if (maxEndAddress < block.EndAddress) + { + maxEndAddress = block.EndAddress; + } - context.MarkLabel(context.GetLabel(block.Address)); + context.CurrBlock = block; - for (int opIndex = 0; opIndex < block.OpCodes.Count; opIndex++) - { - OpCode op = block.OpCodes[opIndex]; + context.MarkLabel(context.GetLabel(block.Address)); - if ((flags & TranslationFlags.DebugMode) != 0) - { - string instName; + EmitOps(context, block); + } - if (op.Emitter != null) - { - instName = op.Emitter.Method.Name; - } - else - { - instName = "???"; + funcs.Add(new FunctionCode(context.GetOperations())); + } - gpuAccessor.Log($"Invalid instruction at 0x{op.Address:X6} (0x{op.RawOpCode:X16})."); - } + config.SizeAdd((int)maxEndAddress + (flags.HasFlag(TranslationFlags.Compute) ? 0 : HeaderSize)); - string dbgComment = $"0x{op.Address:X6}: 0x{op.RawOpCode:X16} {instName}"; + return funcs.ToArray(); + } - context.Add(new CommentNode(dbgComment)); - } + internal static void EmitOps(EmitterContext context, Block block) + { + for (int opIndex = 0; opIndex < block.OpCodes.Count; opIndex++) + { + OpCode op = block.OpCodes[opIndex]; + + if ((context.Config.Flags & TranslationFlags.DebugMode) != 0) + { + string instName; - if (op.NeverExecute) + if (op.Emitter != null) { - continue; + instName = op.Emitter.Method.Name; } + else + { + instName = "???"; - Operand predSkipLbl = null; + context.Config.GpuAccessor.Log($"Invalid instruction at 0x{op.Address:X6} (0x{op.RawOpCode:X16})."); + } - bool skipPredicateCheck = op is OpCodeBranch opBranch && !opBranch.PushTarget; + string dbgComment = $"0x{op.Address:X6}: 0x{op.RawOpCode:X16} {instName}"; - if (op is OpCodeBranchPop opBranchPop) - { - // If the instruction is a SYNC or BRK instruction with only one - // possible target address, then the instruction is basically - // just a simple branch, we can generate code similar to branch - // instructions, with the condition check on the branch itself. - skipPredicateCheck = opBranchPop.Targets.Count < 2; - } + context.Add(new CommentNode(dbgComment)); + } - if (!(op.Predicate.IsPT || skipPredicateCheck)) - { - Operand label; + if (op.NeverExecute) + { + continue; + } - if (opIndex == block.OpCodes.Count - 1 && block.Next != null) - { - label = context.GetLabel(block.Next.Address); - } - else - { - label = Label(); + Operand predSkipLbl = null; - predSkipLbl = label; - } + bool skipPredicateCheck = op is OpCodeBranch opBranch && !opBranch.PushTarget; - Operand pred = Register(op.Predicate); + if (op is OpCodeBranchPop opBranchPop) + { + // If the instruction is a SYNC or BRK instruction with only one + // possible target address, then the instruction is basically + // just a simple branch, we can generate code similar to branch + // instructions, with the condition check on the branch itself. + skipPredicateCheck = opBranchPop.Targets.Count < 2; + } - if (op.InvertPredicate) - { - context.BranchIfTrue(label, pred); - } - else - { - context.BranchIfFalse(label, pred); - } + if (!(op.Predicate.IsPT || skipPredicateCheck)) + { + Operand label; + + if (opIndex == block.OpCodes.Count - 1 && block.Next != null) + { + label = context.GetLabel(block.Next.Address); } + else + { + label = Label(); - context.CurrOp = op; + predSkipLbl = label; + } - op.Emitter?.Invoke(context); + Operand pred = Register(op.Predicate); - if (predSkipLbl != null) + if (op.InvertPredicate) { - context.MarkLabel(predSkipLbl); + context.BranchIfTrue(label, pred); + } + else + { + context.BranchIfFalse(label, pred); } } - } - config.SizeAdd((int)maxEndAddress + (flags.HasFlag(TranslationFlags.Compute) ? 0 : HeaderSize)); + context.CurrOp = op; - return context.GetOperations(); + op.Emitter?.Invoke(context); + + if (predSkipLbl != null) + { + context.MarkLabel(predSkipLbl); + } + } } - private static Operation[] Combine(Operation[] a, Operation[] b) + private static FunctionCode[] Combine(FunctionCode[] a, FunctionCode[] b) { // Here we combine two shaders. // For shader A: @@ -199,15 +261,17 @@ namespace Ryujinx.Graphics.Shader.Translation // For shader B: // - All user attribute loads on shader B are turned into copies from a // temporary variable, as long that attribute is written by shader A. - List<Operation> output = new List<Operation>(a.Length + b.Length); + FunctionCode[] output = new FunctionCode[a.Length + b.Length - 1]; + + List<Operation> ops = new List<Operation>(a.Length + b.Length); Operand[] temps = new Operand[AttributeConsts.UserAttributesCount * 4]; Operand lblB = Label(); - for (int index = 0; index < a.Length; index++) + for (int index = 0; index < a[0].Code.Length; index++) { - Operation operation = a[index]; + Operation operation = a[0].Code[index]; if (IsUserAttribute(operation.Dest)) { @@ -227,19 +291,19 @@ namespace Ryujinx.Graphics.Shader.Translation if (operation.Inst == Instruction.Return) { - output.Add(new Operation(Instruction.Branch, lblB)); + ops.Add(new Operation(Instruction.Branch, lblB)); } else { - output.Add(operation); + ops.Add(operation); } } - output.Add(new Operation(Instruction.MarkLabel, lblB)); + ops.Add(new Operation(Instruction.MarkLabel, lblB)); - for (int index = 0; index < b.Length; index++) + for (int index = 0; index < b[0].Code.Length; index++) { - Operation operation = b[index]; + Operation operation = b[0].Code[index]; for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) { @@ -256,10 +320,22 @@ namespace Ryujinx.Graphics.Shader.Translation } } - output.Add(operation); + ops.Add(operation); + } + + output[0] = new FunctionCode(ops.ToArray()); + + for (int i = 1; i < a.Length; i++) + { + output[i] = a[i]; + } + + for (int i = 1; i < b.Length; i++) + { + output[a.Length + i - 1] = b[i]; } - return output.ToArray(); + return output; } private static bool IsUserAttribute(Operand operand) |