From c9a42f03056c09066b56c0505f633f009681ab68 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 26 Jun 2018 18:01:10 -0300 Subject: [PATCH] Add WIP support for Vertex Program A, add the FADD_I32 shader instruction, small fix on FFMA_I encoding, nits --- Ryujinx.Graphics/Gal/IGalShader.cs | 2 + Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 40 ++++++++++---- Ryujinx.Graphics/Gal/Shader/GlslDecl.cs | 10 ++-- Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 55 +++++++++++++++++-- .../Gal/Shader/ShaderDecodeAlu.cs | 18 ++++++ .../Gal/Shader/ShaderDecodeFlow.cs | 9 ++- .../Gal/Shader/ShaderOpCodeTable.cs | 5 +- Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs | 28 +++++++++- 8 files changed, 144 insertions(+), 23 deletions(-) diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs index 79e77c0a49..06f3fac979 100644 --- a/Ryujinx.Graphics/Gal/IGalShader.cs +++ b/Ryujinx.Graphics/Gal/IGalShader.cs @@ -6,6 +6,8 @@ namespace Ryujinx.Graphics.Gal { void Create(IGalMemory Memory, long Key, GalShaderType Type); + void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type); + IEnumerable GetTextureUsage(long Key); void SetConstBuffer(long Key, int Cbuf, byte[] Data); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 28fa8728ef..3c5c874eaa 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -97,12 +97,37 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Create(IGalMemory Memory, long Key, GalShaderType Type) { - Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, Type)); + Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, 0, false, Type)); } - private ShaderStage ShaderStageFactory(IGalMemory Memory, long Position, GalShaderType Type) + public void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type) { - GlslProgram Program = GetGlslProgram(Memory, Position, Type); + Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, VpAPos, Key, true, Type)); + } + + private ShaderStage ShaderStageFactory( + IGalMemory Memory, + long Position, + long PositionB, + bool IsDualVp, + GalShaderType Type) + { + GlslProgram Program; + + GlslDecompiler Decompiler = new GlslDecompiler(); + + if (IsDualVp) + { + Program = Decompiler.Decompile( + Memory, + Position + 0x50, + PositionB + 0x50, + Type); + } + else + { + Program = Decompiler.Decompile(Memory, Position + 0x50, Type); + } return new ShaderStage( Type, @@ -111,13 +136,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL Program.Uniforms); } - private GlslProgram GetGlslProgram(IGalMemory Memory, long Position, GalShaderType Type) - { - GlslDecompiler Decompiler = new GlslDecompiler(); - - return Decompiler.Decompile(Memory, Position + 0x50, Type); - } - public IEnumerable GetTextureUsage(long Key) { if (Stages.TryGetValue(Key, out ShaderStage Stage)) @@ -283,7 +301,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL return FreeBinding; } - + BindUniformBuffersIfNotNull(Current.Vertex); BindUniformBuffersIfNotNull(Current.TessControl); BindUniformBuffersIfNotNull(Current.TessEvaluation); diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index 86838ab2e4..5531aa46f7 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -28,12 +28,13 @@ namespace Ryujinx.Graphics.Gal.Shader public const string FlipUniformName = "flip"; + public const string StageProgramBName = "program_b"; + private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" }; private string StagePrefix; private Dictionary m_Textures; - private Dictionary m_Uniforms; private Dictionary m_InAttributes; @@ -43,7 +44,6 @@ namespace Ryujinx.Graphics.Gal.Shader private Dictionary m_Preds; public IReadOnlyDictionary Textures => m_Textures; - public IReadOnlyDictionary Uniforms => m_Uniforms; public IReadOnlyDictionary InAttributes => m_InAttributes; @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Gal.Shader public GalShaderType ShaderType { get; private set; } - public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType) + public GlslDecl(GalShaderType ShaderType) { this.ShaderType = ShaderType; @@ -80,7 +80,10 @@ namespace Ryujinx.Graphics.Gal.Shader { m_OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4)); } + } + public void Add(ShaderIrBlock[] Blocks) + { foreach (ShaderIrBlock Block in Blocks) { foreach (ShaderIrNode Node in Block.GetNodes()) @@ -89,7 +92,6 @@ namespace Ryujinx.Graphics.Gal.Shader } } } - private void Traverse(ShaderIrNode Parent, ShaderIrNode Node) { switch (Node) diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 576358c79c..5389c0843d 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -25,10 +25,12 @@ namespace Ryujinx.Graphics.Gal.Shader private GlslDecl Decl; - private ShaderIrBlock[] Blocks; + private ShaderIrBlock[] Blocks, BlocksB; private StringBuilder SB; + private bool NeedsProgramBCall; + public GlslDecompiler() { InstsExpr = new Dictionary() @@ -104,12 +106,37 @@ namespace Ryujinx.Graphics.Gal.Shader }; } + public GlslProgram Decompile( + IGalMemory Memory, + long VpAPosition, + long VpBPosition, + GalShaderType ShaderType) + { + Blocks = ShaderDecoder.Decode(Memory, VpAPosition); + BlocksB = ShaderDecoder.Decode(Memory, VpBPosition); + + Decl = new GlslDecl(ShaderType); + + Decl.Add(Blocks); + Decl.Add(BlocksB); + + return Decompile(); + } + public GlslProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) { - Blocks = ShaderDecoder.Decode(Memory, Position); + Blocks = ShaderDecoder.Decode(Memory, Position); + BlocksB = null; - Decl = new GlslDecl(Blocks, ShaderType); + Decl = new GlslDecl(ShaderType); + Decl.Add(Blocks); + + return Decompile(); + } + + private GlslProgram Decompile() + { SB = new StringBuilder(); SB.AppendLine("#version 410 core"); @@ -121,8 +148,21 @@ namespace Ryujinx.Graphics.Gal.Shader PrintDeclGprs(); PrintDeclPreds(); + if (BlocksB != null) + { + string SubName = "void " + GlslDecl.StageProgramBName + "()"; + + PrintBlockScope(BlocksB[0], null, null, SubName, IdentationStr); + + SB.AppendLine(); + + NeedsProgramBCall = true; + } + PrintBlockScope(Blocks[0], null, null, "void main()", IdentationStr); + NeedsProgramBCall = false; + string GlslCode = SB.ToString(); return new GlslProgram( @@ -146,8 +186,10 @@ namespace Ryujinx.Graphics.Gal.Shader foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) { SB.AppendLine($"layout (std140) uniform {DeclInfo.Name} {{"); + SB.AppendLine($"{IdentationStr}vec4 {DeclInfo.Name}_data[{DeclInfo.Index / 4 + 1}];"); - SB.AppendLine($"}};"); + + SB.AppendLine("};"); } if (Decl.Uniforms.Count > 0) @@ -389,6 +431,11 @@ namespace Ryujinx.Graphics.Gal.Shader //the shader ends here. if (Decl.ShaderType == GalShaderType.Vertex) { + if (NeedsProgramBCall) + { + SB.AppendLine(Identation + GlslDecl.StageProgramBName + "();"); + } + SB.AppendLine(Identation + "gl_Position.xy *= flip;"); SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;"); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 0763d3caf6..2e022fbd44 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -31,6 +31,24 @@ namespace Ryujinx.Graphics.Gal.Shader EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fadd); } + public static void Fadd_I32(ShaderIrBlock Block, long OpCode) + { + ShaderIrNode OperA = GetOperGpr8 (OpCode); + ShaderIrNode OperB = GetOperImmf32_20(OpCode); + + bool NegB = ((OpCode >> 53) & 1) != 0; + bool AbsA = ((OpCode >> 54) & 1) != 0; + bool NegA = ((OpCode >> 56) & 1) != 0; + bool AbsB = ((OpCode >> 57) & 1) != 0; + + OperA = GetAluFabsFneg(OperA, AbsA, NegA); + OperB = GetAluFabsFneg(OperB, AbsB, NegB); + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + public static void Fadd_R(ShaderIrBlock Block, long OpCode) { EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fadd); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs index 89949d62ee..8d0925a321 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs @@ -24,7 +24,14 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Exit(ShaderIrBlock Block, long OpCode) { - Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Exit), OpCode)); + int CCode = (int)OpCode & 0x1f; + + //TODO: Figure out what the other condition codes mean... + if (CCode == 0xf) + { + Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Exit), OpCode)); + } + } public static void Kil(ShaderIrBlock Block, long OpCode) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 074cfbb28b..b4f51e5084 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -39,14 +39,15 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0101110010110x", ShaderDecode.F2i_R); Set("0100110001011x", ShaderDecode.Fadd_C); Set("0011100x01011x", ShaderDecode.Fadd_I); + Set("000010xxxxxxxx", ShaderDecode.Fadd_I32); Set("0101110001011x", ShaderDecode.Fadd_R); Set("010010011xxxxx", ShaderDecode.Ffma_CR); - Set("001100101xxxxx", ShaderDecode.Ffma_I); + Set("0011001x1xxxxx", ShaderDecode.Ffma_I); Set("010100011xxxxx", ShaderDecode.Ffma_RC); Set("010110011xxxxx", ShaderDecode.Ffma_RR); - Set("00011110xxxxxx", ShaderDecode.Fmul_I32); Set("0100110001101x", ShaderDecode.Fmul_C); Set("0011100x01101x", ShaderDecode.Fmul_I); + Set("00011110xxxxxx", ShaderDecode.Fmul_I32); Set("0101110001101x", ShaderDecode.Fmul_R); Set("0100110001100x", ShaderDecode.Fmnmx_C); Set("0011100x01100x", ShaderDecode.Fmnmx_I); diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index 02461d5dda..d382154e12 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -125,7 +125,33 @@ namespace Ryujinx.HLE.Gpu.Engines long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - for (int Index = 0; Index < 6; Index++) + int Index = 1; + + int VpAControl = ReadRegister(NvGpuEngine3dReg.ShaderNControl); + + bool VpAEnable = (VpAControl & 1) != 0; + + if (VpAEnable) + { + //Note: The maxwell supports 2 vertex programs, usually + //only VP B is used, but in some cases VP A is also used. + //In this case, it seems to function as an extra vertex + //shader stage. + //The graphics abstraction layer has a special overload for this + //case, which should merge the two shaders into one vertex shader. + int VpAOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset); + int VpBOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + 0x10); + + long VpAPos = BasePosition + (uint)VpAOffset; + long VpBPos = BasePosition + (uint)VpBOffset; + + Gpu.Renderer.Shader.Create(Vmm, VpAPos, VpBPos, GalShaderType.Vertex); + Gpu.Renderer.Shader.Bind(VpBPos); + + Index = 2; + } + + for (; Index < 6; Index++) { int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10); int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + Index * 0x10);