diff --git a/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs b/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs new file mode 100644 index 0000000000..8565051cac --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ryujinx.Graphics.Gal +{ + [Flags] + public enum GalClearBufferFlags + { + Depth = 1 << 0, + Stencil = 1 << 1, + ColorRed = 1 << 2, + ColorGreen = 1 << 3, + ColorBlue = 1 << 4, + ColorAlpha = 1 << 5 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs index dc38c59345..563e624d53 100644 --- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs +++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs @@ -2,8 +2,6 @@ namespace Ryujinx.Graphics.Gal { public struct GalVertexAttrib { - public int Index { get; private set; } - public int Buffer { get; private set; } public bool IsConst { get; private set; } public int Offset { get; private set; } @@ -13,16 +11,12 @@ namespace Ryujinx.Graphics.Gal public bool IsBgra { get; private set; } public GalVertexAttrib( - int Index, - int Buffer, bool IsConst, int Offset, GalVertexAttribSize Size, GalVertexAttribType Type, bool IsBgra) { - this.Index = Index; - this.Buffer = Buffer; this.IsConst = IsConst; this.Offset = Offset; this.Size = Size; diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 79d35efcb6..bafa16bd46 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -21,12 +21,17 @@ namespace Ryujinx.Graphics.Gal float OffsY, float Rotate); + //Rasterizer + void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); + + void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs); + void RenderVertexArray(int VbIndex); + //Shader - void CreateShader(long Tag, byte[] Data, GalShaderType Type); + void CreateShader(long Tag, GalShaderType Type, byte[] Data); void SetShaderCb(long Tag, int Cbuf, byte[] Data); void BindShader(long Tag); - - void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs); + void BindProgram(); void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height); diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs index b761811f64..7e7725d611 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs @@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL PixelFormat.Rgba, PixelType.UnsignedByte, Pixels); - + GL.ActiveTexture(TextureUnit.Texture0); GL.BindVertexArray(VaoHandle); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 4606489926..235bbc6874 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -1,7 +1,178 @@ +using OpenTK.Graphics.OpenGL; +using System; +using System.Collections.Generic; + namespace Ryujinx.Graphics.Gal.OpenGL { class OGLRasterizer { - + private static Dictionary AttribElements = + new Dictionary() + { + { GalVertexAttribSize._32_32_32_32, 4 }, + { GalVertexAttribSize._32_32_32, 3 }, + { GalVertexAttribSize._16_16_16_16, 4 }, + { GalVertexAttribSize._32_32, 2 }, + { GalVertexAttribSize._16_16_16, 3 }, + { GalVertexAttribSize._8_8_8_8, 4 }, + { GalVertexAttribSize._16_16, 2 }, + { GalVertexAttribSize._32, 1 }, + { GalVertexAttribSize._8_8_8, 3 }, + { GalVertexAttribSize._8_8, 2 }, + { GalVertexAttribSize._16, 1 }, + { GalVertexAttribSize._8, 1 }, + { GalVertexAttribSize._10_10_10_2, 4 }, + { GalVertexAttribSize._11_11_10, 3 } + }; + + private static Dictionary AttribTypes = + new Dictionary() + { + { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int }, //? + { GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //? + }; + + private struct VertexBuffer + { + public int VaoHandle; + public int VboHandle; + + public int PrimCount; + } + + private VertexBuffer[] VertexBuffers; + + public OGLRasterizer() + { + VertexBuffers = new VertexBuffer[32]; + } + + public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) + { + ClearBufferMask Mask = 0; + + //OpenGL doesn't support clearing just a single color channel, + //so we can't just clear all channels... + if (Flags.HasFlag(GalClearBufferFlags.ColorRed) && + Flags.HasFlag(GalClearBufferFlags.ColorGreen) && + Flags.HasFlag(GalClearBufferFlags.ColorBlue) && + Flags.HasFlag(GalClearBufferFlags.ColorAlpha)) + { + Mask = ClearBufferMask.ColorBufferBit; + } + + if (Flags.HasFlag(GalClearBufferFlags.Depth)) + { + Mask |= ClearBufferMask.DepthBufferBit; + } + + if (Flags.HasFlag(GalClearBufferFlags.Stencil)) + { + Mask |= ClearBufferMask.StencilBufferBit; + } + + GL.Clear(Mask); + } + + public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs) + { + EnsureVbInitialized(VbIndex); + + VertexBuffers[VbIndex].PrimCount = Buffer.Length / Stride; + + VertexBuffer Vb = VertexBuffers[VbIndex]; + + IntPtr Length = new IntPtr(Buffer.Length); + + GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle); + GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + + GL.BindVertexArray(Vb.VaoHandle); + + for (int Attr = 0; Attr < 16; Attr++) + { + GL.DisableVertexAttribArray(Attr); + } + + for (int Index = 0; Index < Attribs.Length; Index++) + { + GalVertexAttrib Attrib = Attribs[Index]; + + GL.EnableVertexAttribArray(Index); + + GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle); + + bool Unsigned = + Attrib.Type == GalVertexAttribType.Unorm || + Attrib.Type == GalVertexAttribType.Uint || + Attrib.Type == GalVertexAttribType.Uscaled; + + bool Normalize = + Attrib.Type == GalVertexAttribType.Snorm || + Attrib.Type == GalVertexAttribType.Unorm; + + VertexAttribPointerType Type = 0; + + if (Attrib.Type == GalVertexAttribType.Float) + { + Type = VertexAttribPointerType.Float; + } + else + { + Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0); + } + + int Size = AttribElements[Attrib.Size]; + int Offset = Attrib.Offset; + + GL.VertexAttribPointer(Index, Size, Type, Normalize, Stride, Offset); + } + + GL.BindVertexArray(0); + } + + public void RenderVertexArray(int VbIndex) + { + VertexBuffer Vb = VertexBuffers[VbIndex]; + + if (Vb.PrimCount == 0) + { + return; + } + + GL.BindVertexArray(Vb.VaoHandle); + + GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount); + } + + private void EnsureVbInitialized(int VbIndex) + { + VertexBuffer Vb = VertexBuffers[VbIndex]; + + if (Vb.VaoHandle == 0) + { + Vb.VaoHandle = GL.GenVertexArray(); + } + + if (Vb.VboHandle == 0) + { + Vb.VboHandle = GL.GenBuffer(); + } + + VertexBuffers[VbIndex] = Vb; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index b286db667c..16d5a87e83 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -15,13 +15,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL public GalShaderType Type { get; private set; } - public ICollection TextureUsage { get; private set; } - public ICollection UniformUsage { get; private set; } + public IEnumerable TextureUsage { get; private set; } + public IEnumerable UniformUsage { get; private set; } public ShaderStage( GalShaderType Type, - ICollection TextureUsage, - ICollection UniformUsage) + IEnumerable TextureUsage, + IEnumerable UniformUsage) { Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); @@ -61,6 +61,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL private Dictionary Programs; + public int CurrentProgramHandle { get; private set; } + public OGLShader() { Stages = new Dictionary(); @@ -68,14 +70,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL Programs = new Dictionary(); } - public void Create(long Tag, byte[] Data, GalShaderType Type) + public void Create(long Tag, GalShaderType Type, byte[] Data) { if (!Stages.ContainsKey(Tag)) { GlslProgram Program = GetGlslProgram(Data, Type); - System.Console.WriteLine(Program.Code); - ShaderStage Stage = new ShaderStage( Type, Program.Textures, @@ -94,6 +94,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf)) { float Value = BitConverter.ToSingle(Data, DeclInfo.Index * 4); + + int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name); + + GL.Uniform1(Location, Value); } } } @@ -113,21 +117,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - public bool BindCurrentProgram() + public void BindProgram() { if (Current.Vertex == null || Current.Fragment == null) { - return false; + return; } - GL.UseProgram(GetCurrentProgramHandle()); - - return true; - } - - private int GetCurrentProgramHandle() - { if (!Programs.TryGetValue(Current, out int Handle)) { Handle = GL.CreateProgram(); @@ -145,7 +142,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL Programs.Add(Current, Handle); } - return Handle; + GL.UseProgram(Handle); + + CurrentProgramHandle = Handle; } private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 1a385ebc7d..a8e2e79022 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -2,29 +2,20 @@ using OpenTK; using OpenTK.Graphics.OpenGL; using System; using System.Collections.Concurrent; -using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { public class OpenGLRenderer : IGalRenderer { - private struct VertexBuffer - { - public int VaoHandle; - public int VboHandle; - - public int PrimCount; - } - private struct Texture { public int Handle; } - private List VertexBuffers; - private Texture[] Textures; + private OGLRasterizer Rasterizer; + private OGLShader Shader; private ConcurrentQueue ActionsQueue; @@ -33,10 +24,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL public OpenGLRenderer() { - VertexBuffers = new List(); - Textures = new Texture[8]; + Rasterizer = new OGLRasterizer(); + Shader = new OGLShader(); ActionsQueue = new ConcurrentQueue(); @@ -70,18 +61,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Render() { FbRenderer.Render(); - - for (int Index = 0; Index < VertexBuffers.Count; Index++) - { - VertexBuffer Vb = VertexBuffers[Index]; - - if (Vb.VaoHandle != 0 && - Vb.PrimCount != 0) - { - GL.BindVertexArray(Vb.VaoHandle); - GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount); - } - } } public void SetWindowSize(int Width, int Height) @@ -110,157 +89,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL FbRenderer.Set(Fb, Width, Height, Transform, Offs); } - public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs) + public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) { - if (Index < 0) + ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags)); + } + + public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs) + { + if ((uint)VbIndex > 31) { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(VbIndex)); } - if (Buffer.Length == 0 || Stride == 0) + ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride, + Buffer ?? throw new ArgumentNullException(nameof(Buffer)), + Attribs ?? throw new ArgumentNullException(nameof(Attribs)))); + } + + public void RenderVertexArray(int VbIndex) + { + if ((uint)VbIndex > 31) { - return; + throw new ArgumentOutOfRangeException(nameof(VbIndex)); } - EnsureVbInitialized(Index); - - VertexBuffer Vb = VertexBuffers[Index]; - - Vb.PrimCount = Buffer.Length / Stride; - - VertexBuffers[Index] = Vb; - - IntPtr Length = new IntPtr(Buffer.Length); - - GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle); - GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); - GL.BindBuffer(BufferTarget.ArrayBuffer, 0); - - GL.BindVertexArray(Vb.VaoHandle); - - for (int Attr = 0; Attr < 16; Attr++) - { - GL.DisableVertexAttribArray(Attr); - } - - foreach (GalVertexAttrib Attrib in Attribs) - { - if (Attrib.Index >= 3) break; - - GL.EnableVertexAttribArray(Attrib.Index); - - GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle); - - int Size = 0; - - switch (Attrib.Size) - { - case GalVertexAttribSize._8: - case GalVertexAttribSize._16: - case GalVertexAttribSize._32: - Size = 1; - break; - case GalVertexAttribSize._8_8: - case GalVertexAttribSize._16_16: - case GalVertexAttribSize._32_32: - Size = 2; - break; - case GalVertexAttribSize._8_8_8: - case GalVertexAttribSize._11_11_10: - case GalVertexAttribSize._16_16_16: - case GalVertexAttribSize._32_32_32: - Size = 3; - break; - case GalVertexAttribSize._8_8_8_8: - case GalVertexAttribSize._10_10_10_2: - case GalVertexAttribSize._16_16_16_16: - case GalVertexAttribSize._32_32_32_32: - Size = 4; - break; - } - - bool Signed = - Attrib.Type == GalVertexAttribType.Snorm || - Attrib.Type == GalVertexAttribType.Sint || - Attrib.Type == GalVertexAttribType.Sscaled; - - bool Normalize = - Attrib.Type == GalVertexAttribType.Snorm || - Attrib.Type == GalVertexAttribType.Unorm; - - VertexAttribPointerType Type = 0; - - switch (Attrib.Type) - { - case GalVertexAttribType.Snorm: - case GalVertexAttribType.Unorm: - case GalVertexAttribType.Sint: - case GalVertexAttribType.Uint: - case GalVertexAttribType.Uscaled: - case GalVertexAttribType.Sscaled: - { - switch (Attrib.Size) - { - case GalVertexAttribSize._8: - case GalVertexAttribSize._8_8: - case GalVertexAttribSize._8_8_8: - case GalVertexAttribSize._8_8_8_8: - { - Type = Signed - ? VertexAttribPointerType.Byte - : VertexAttribPointerType.UnsignedByte; - - break; - } - - case GalVertexAttribSize._16: - case GalVertexAttribSize._16_16: - case GalVertexAttribSize._16_16_16: - case GalVertexAttribSize._16_16_16_16: - { - Type = Signed - ? VertexAttribPointerType.Short - : VertexAttribPointerType.UnsignedShort; - - break; - } - - case GalVertexAttribSize._10_10_10_2: - case GalVertexAttribSize._11_11_10: - case GalVertexAttribSize._32: - case GalVertexAttribSize._32_32: - case GalVertexAttribSize._32_32_32: - case GalVertexAttribSize._32_32_32_32: - { - Type = Signed - ? VertexAttribPointerType.Int - : VertexAttribPointerType.UnsignedInt; - - break; - } - } - - break; - } - - case GalVertexAttribType.Float: - { - Type = VertexAttribPointerType.Float; - - break; - } - } - - GL.VertexAttribPointer( - Attrib.Index, - Size, - Type, - Normalize, - Stride, - Attrib.Offset); - } - - GL.BindVertexArray(0); + ActionsQueue.Enqueue(() => Rasterizer.RenderVertexArray(VbIndex)); } public void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height) @@ -290,14 +143,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle); } - public void CreateShader(long Tag, byte[] Data, GalShaderType Type) + public void CreateShader(long Tag, GalShaderType Type, byte[] Data) { if (Data == null) { throw new ArgumentNullException(nameof(Data)); } - ActionsQueue.Enqueue(() => Shader.Create(Tag, Data, Type)); + ActionsQueue.Enqueue(() => Shader.Create(Tag, Type, Data)); } public void SetShaderCb(long Tag, int Cbuf, byte[] Data) @@ -315,26 +168,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => Shader.Bind(Tag)); } - private void EnsureVbInitialized(int VbIndex) + public void BindProgram() { - while (VbIndex >= VertexBuffers.Count) - { - VertexBuffers.Add(new VertexBuffer()); - } - - VertexBuffer Vb = VertexBuffers[VbIndex]; - - if (Vb.VaoHandle == 0) - { - Vb.VaoHandle = GL.GenVertexArray(); - } - - if (Vb.VboHandle == 0) - { - Vb.VboHandle = GL.GenBuffer(); - } - - VertexBuffers[VbIndex] = Vb; + ActionsQueue.Enqueue(() => Shader.BindProgram()); } private void EnsureTexInitialized(int TexIndex) diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs new file mode 100644 index 0000000000..e957d13c63 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -0,0 +1,203 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader +{ + class GlslDecl + { + private const int AttrStartIndex = 8; + private const int TexStartIndex = 8; + + private const string InAttrName = "in_attr"; + private const string OutAttrName = "out_attr"; + private const string UniformName = "c"; + + private const string GprName = "gpr"; + private const string PredName = "pred"; + private const string TextureName = "tex"; + + public const string FragmentOutputName = "FragColor"; + + private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" }; + + private string StagePrefix; + + private Dictionary m_Textures; + + private Dictionary<(int, int), ShaderDeclInfo> m_Uniforms; + + private Dictionary m_InAttributes; + private Dictionary m_OutAttributes; + + private Dictionary m_Gprs; + private Dictionary m_Preds; + + public IReadOnlyDictionary Textures => m_Textures; + + public IReadOnlyDictionary<(int, int), ShaderDeclInfo> Uniforms => m_Uniforms; + + public IReadOnlyDictionary InAttributes => m_InAttributes; + public IReadOnlyDictionary OutAttributes => m_OutAttributes; + + public IReadOnlyDictionary Gprs => m_Gprs; + public IReadOnlyDictionary Preds => m_Preds; + + public GalShaderType ShaderType { get; private set; } + + public GlslDecl(ShaderIrNode[] Nodes, GalShaderType ShaderType) + { + this.ShaderType = ShaderType; + + StagePrefix = StagePrefixes[(int)ShaderType] + "_"; + + m_Uniforms = new Dictionary<(int, int), ShaderDeclInfo>(); + + m_Textures = new Dictionary(); + + m_InAttributes = new Dictionary(); + m_OutAttributes = new Dictionary(); + + m_Gprs = new Dictionary(); + m_Preds = new Dictionary(); + + //FIXME: Only valid for vertex shaders. + if (ShaderType == GalShaderType.Fragment) + { + m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4)); + } + else + { + m_OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4)); + } + + foreach (ShaderIrNode Node in Nodes) + { + Traverse(null, Node); + } + } + + private void Traverse(ShaderIrNode Parent, ShaderIrNode Node) + { + switch (Node) + { + case ShaderIrAsg Asg: + { + Traverse(Asg, Asg.Dst); + Traverse(Asg, Asg.Src); + + break; + } + + case ShaderIrCond Cond: + { + Traverse(Cond, Cond.Pred); + Traverse(Cond, Cond.Child); + + break; + } + + case ShaderIrOp Op: + { + Traverse(Op, Op.OperandA); + Traverse(Op, Op.OperandB); + Traverse(Op, Op.OperandC); + + if (Op.Inst == ShaderIrInst.Texr || + Op.Inst == ShaderIrInst.Texg || + Op.Inst == ShaderIrInst.Texb || + Op.Inst == ShaderIrInst.Texa) + { + int Handle = ((ShaderIrOperImm)Op.OperandC).Imm; + + int Index = Handle - TexStartIndex; + + string Name = StagePrefix + TextureName + Index; + + m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Index)); + } + break; + } + + case ShaderIrOperCbuf Cbuf: + { + string Name = StagePrefix + UniformName + Cbuf.Index + "_" + Cbuf.Offs; + + ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Offs, Cbuf.Index); + + m_Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo); + + break; + } + + case ShaderIrOperAbuf Abuf: + { + int Index = Abuf.Offs >> 4; + int Elem = (Abuf.Offs >> 2) & 3; + + int GlslIndex = Index - AttrStartIndex; + + ShaderDeclInfo DeclInfo; + + if (Parent is ShaderIrAsg Asg && Asg.Dst == Node) + { + if (!m_OutAttributes.TryGetValue(Index, out DeclInfo)) + { + DeclInfo = new ShaderDeclInfo(OutAttrName + GlslIndex, GlslIndex); + + m_OutAttributes.Add(Index, DeclInfo); + } + } + else + { + if (!m_InAttributes.TryGetValue(Index, out DeclInfo)) + { + DeclInfo = new ShaderDeclInfo(InAttrName + GlslIndex, GlslIndex); + + m_InAttributes.Add(Index, DeclInfo); + } + } + + DeclInfo.Enlarge(Elem + 1); + + break; + } + + case ShaderIrOperGpr Gpr: + { + if (!Gpr.IsConst && !HasName(m_Gprs, Gpr.Index)) + { + string Name = GprName + Gpr.Index; + + m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index)); + } + break; + } + + case ShaderIrOperPred Pred: + { + if (!Pred.IsConst && !HasName(m_Preds, Pred.Index)) + { + string Name = PredName + Pred.Index; + + m_Preds.TryAdd(Pred.Index, new ShaderDeclInfo(Name, Pred.Index)); + } + break; + } + } + } + + private bool HasName(Dictionary Decls, int Index) + { + int VecIndex = Index >> 2; + + if (Decls.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo)) + { + if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) + { + return true; + } + } + + return Decls.ContainsKey(Index); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 0fa5d3a151..0f89eb2f0d 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -16,26 +16,7 @@ namespace Ryujinx.Graphics.Gal.Shader private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; - private Dictionary Textures; - - private Dictionary<(int, int), ShaderDeclInfo> Uniforms; - - private Dictionary InAttributes; - private Dictionary OutAttributes; - - private Dictionary Gprs; - private Dictionary Preds; - - private const int AttrStartIndex = 8; - private const int TexStartIndex = 8; - - private const string InputAttrName = "in_attr"; - private const string OutputName = "out_attr"; - private const string UniformName = "c"; - - private const string GprName = "gpr"; - private const string PredName = "pred"; - private const string TextureName = "tex"; + private GlslDecl Decl; private StringBuilder SB; @@ -72,38 +53,15 @@ namespace Ryujinx.Graphics.Gal.Shader }; } - public GlslProgram Decompile(int[] Code, GalShaderType Type) + public GlslProgram Decompile(int[] Code, GalShaderType ShaderType) { - Uniforms = new Dictionary<(int, int), ShaderDeclInfo>(); - - Textures = new Dictionary(); - - InAttributes = new Dictionary(); - OutAttributes = new Dictionary(); - - Gprs = new Dictionary(); - Preds = new Dictionary(); - - SB = new StringBuilder(); - - //FIXME: Only valid for vertex shaders. - if (Type == GalShaderType.Fragment) - { - Gprs.Add(0, new ShaderDeclInfo("FragColor", 0, 0, 4)); - } - else - { - OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4)); - } - - ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, Type); + ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType); ShaderIrNode[] Nodes = Block.GetNodes(); - foreach (ShaderIrNode Node in Nodes) - { - Traverse(null, Node); - } + Decl = new GlslDecl(Nodes, ShaderType); + + SB = new StringBuilder(); SB.AppendLine("#version 430"); @@ -120,133 +78,23 @@ namespace Ryujinx.Graphics.Gal.Shader return new GlslProgram( GlslCode, - Textures.Values, - Uniforms.Values); - } - - private void Traverse(ShaderIrNode Parent, ShaderIrNode Node) - { - switch (Node) - { - case ShaderIrAsg Asg: - { - Traverse(Asg, Asg.Dst); - Traverse(Asg, Asg.Src); - - break; - } - - case ShaderIrCond Cond: - { - Traverse(Cond, Cond.Pred); - Traverse(Cond, Cond.Child); - - break; - } - - case ShaderIrOp Op: - { - Traverse(Op, Op.OperandA); - Traverse(Op, Op.OperandB); - Traverse(Op, Op.OperandC); - - if (Op.Inst == ShaderIrInst.Texr || - Op.Inst == ShaderIrInst.Texg || - Op.Inst == ShaderIrInst.Texb || - Op.Inst == ShaderIrInst.Texa) - { - int Handle = ((ShaderIrOperImm)Op.OperandC).Imm; - - int Index = Handle - TexStartIndex; - - string Name = $"{TextureName}{Index}"; - - Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Index)); - } - break; - } - - case ShaderIrOperCbuf Cbuf: - { - string Name = $"{UniformName}{Cbuf.Index}_{Cbuf.Offs}"; - - ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Offs, Cbuf.Index); - - Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo); - - break; - } - - case ShaderIrOperAbuf Abuf: - { - int Index = Abuf.Offs >> 4; - int Elem = (Abuf.Offs >> 2) & 3; - - int GlslIndex = Index - AttrStartIndex; - - ShaderDeclInfo DeclInfo; - - if (Parent is ShaderIrAsg Asg && Asg.Dst == Node) - { - if (!OutAttributes.TryGetValue(Index, out DeclInfo)) - { - DeclInfo = new ShaderDeclInfo(OutputName + GlslIndex, GlslIndex); - - OutAttributes.Add(Index, DeclInfo); - } - } - else - { - if (!InAttributes.TryGetValue(Index, out DeclInfo)) - { - DeclInfo = new ShaderDeclInfo(InputAttrName + GlslIndex, GlslIndex); - - InAttributes.Add(Index, DeclInfo); - } - } - - DeclInfo.Enlarge(Elem + 1); - - break; - } - - case ShaderIrOperGpr Gpr: - { - if (!Gpr.IsConst && GetNameWithSwizzle(Gprs, Gpr.Index) == null) - { - string Name = $"{GprName}{Gpr.Index}"; - - Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index)); - } - break; - } - - case ShaderIrOperPred Pred: - { - if (!Pred.IsConst && GetNameWithSwizzle(Preds, Pred.Index) == null) - { - string Name = $"{PredName}{Pred.Index}"; - - Preds.TryAdd(Pred.Index, new ShaderDeclInfo(Name, Pred.Index)); - } - break; - } - } + Decl.Textures.Values, + Decl.Uniforms.Values); } private void PrintDeclTextures() { - PrintDecls(Textures.Values, "uniform sampler2D"); + PrintDecls(Decl.Textures, "uniform sampler2D"); } private void PrintDeclUniforms() { - foreach (ShaderDeclInfo DeclInfo in Uniforms.Values.OrderBy(DeclKeySelector)) + foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) { SB.AppendLine($"uniform {GetDecl(DeclInfo)};"); } - if (Uniforms.Values.Count > 0) + if (Decl.Uniforms.Count > 0) { SB.AppendLine(); } @@ -254,17 +102,17 @@ namespace Ryujinx.Graphics.Gal.Shader private void PrintDeclInAttributes() { - PrintDeclAttributes(InAttributes.Values, "in"); + PrintDeclAttributes(Decl.InAttributes.Values, "in"); } private void PrintDeclOutAttributes() { - PrintDeclAttributes(OutAttributes.Values, "out"); + PrintDeclAttributes(Decl.OutAttributes.Values, "out"); } - private void PrintDeclAttributes(ICollection Decls, string InOut) + private void PrintDeclAttributes(IEnumerable Decls, string InOut) { - bool PrintNl = false; + int Count = 0; foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector)) { @@ -272,11 +120,11 @@ namespace Ryujinx.Graphics.Gal.Shader { SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};"); - PrintNl = true; + Count++; } } - if (PrintNl) + if (Count > 0) { SB.AppendLine(); } @@ -284,33 +132,37 @@ namespace Ryujinx.Graphics.Gal.Shader private void PrintDeclGprs() { - PrintDecls(Gprs.Values); + PrintDecls(Decl.Gprs); } private void PrintDeclPreds() { - PrintDecls(Preds.Values, "bool"); + PrintDecls(Decl.Preds, "bool"); } - private void PrintDecls(ICollection Decls, string CustomType = null) + private void PrintDecls(IReadOnlyDictionary Dict, string CustomType = null) { - foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector)) + foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector)) { string Name; if (CustomType != null) { - Name = $"{CustomType} {DeclInfo.Name};"; + Name = CustomType + " " + DeclInfo.Name + ";"; + } + else if (DeclInfo.Name == GlslDecl.FragmentOutputName) + { + Name = "out " + GetDecl(DeclInfo) + ";"; } else { - Name = $"{GetDecl(DeclInfo)};"; + Name = GetDecl(DeclInfo) + ";"; } SB.AppendLine(Name); } - if (Decls.Count > 0) + if (Dict.Count > 0) { SB.AppendLine(); } @@ -323,7 +175,7 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetDecl(ShaderDeclInfo DeclInfo) { - return $"{ElemTypes[DeclInfo.Size - 1]} {DeclInfo.Name}"; + return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name; } private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes) @@ -355,17 +207,17 @@ namespace Ryujinx.Graphics.Gal.Shader if (Node is ShaderIrCond Cond) { - string SubScopeName = $"if ({GetInOperName(Cond.Pred, true)})"; + string SubScopeName = "if (" + GetInOperName(Cond.Pred, true) + ")"; PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child); } else if (Node is ShaderIrAsg Asg && IsValidOutOper(Asg.Dst)) { - SB.AppendLine($"{Identation}{GetOutOperName(Asg.Dst)} = {GetInOperName(Asg.Src, true)};"); + SB.AppendLine(Identation + GetOutOperName(Asg.Dst) + " = " + GetInOperName(Asg.Src, true) + ";"); } else if (Node is ShaderIrOp Op) { - SB.AppendLine($"{Identation}{GetInOperName(Op, true)};"); + SB.AppendLine(Identation + GetInOperName(Op, true) + ";"); } else { @@ -432,7 +284,7 @@ namespace Ryujinx.Graphics.Gal.Shader if (!(Entry || IsUnary(Op.Inst))) { - Expr = $"({Expr})"; + Expr = "(" + Expr + ")"; } return Expr; @@ -461,7 +313,7 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetName(ShaderIrOperCbuf Cbuf) { - if (!Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo)) + if (!Decl.Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo)) { throw new InvalidOperationException(); } @@ -471,15 +323,15 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetOutAbufName(ShaderIrOperAbuf Abuf) { - return GetName(OutAttributes, Abuf, Swizzle: true); + return GetName(Decl.OutAttributes, Abuf); } - private string GetName(ShaderIrOperAbuf Abuf, bool Swizzle = true) + private string GetName(ShaderIrOperAbuf Abuf) { - return GetName(InAttributes, Abuf, Swizzle); + return GetName(Decl.InAttributes, Abuf); } - private string GetName(Dictionary Decls, ShaderIrOperAbuf Abuf, bool Swizzle) + private string GetName(IReadOnlyDictionary Decls, ShaderIrOperAbuf Abuf) { int Index = Abuf.Offs >> 4; int Elem = (Abuf.Offs >> 2) & 3; @@ -489,14 +341,12 @@ namespace Ryujinx.Graphics.Gal.Shader throw new InvalidOperationException(); } - Swizzle &= DeclInfo.Size > 1; - - return Swizzle ? $"{DeclInfo.Name}.{GetAttrSwizzle(Elem)}" : DeclInfo.Name; + return DeclInfo.Size > 1 ? DeclInfo.Name + "." + GetAttrSwizzle(Elem) : DeclInfo.Name; } private string GetName(ShaderIrOperGpr Gpr) { - return Gpr.IsConst ? "0" : GetNameWithSwizzle(Gprs, Gpr.Index); + return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index); } private string GetName(ShaderIrOperImm Imm) @@ -506,10 +356,10 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetName(ShaderIrOperPred Pred) { - return Pred.IsConst ? "true" : GetNameWithSwizzle(Preds, Pred.Index); + return Pred.IsConst ? "true" : GetNameWithSwizzle(Decl.Preds, Pred.Index); } - private string GetNameWithSwizzle(Dictionary Decls, int Index) + private string GetNameWithSwizzle(IReadOnlyDictionary Decls, int Index) { int VecIndex = Index >> 2; @@ -517,137 +367,82 @@ namespace Ryujinx.Graphics.Gal.Shader { if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) { - return $"{DeclInfo.Name}.{GetAttrSwizzle(Index & 3)}"; + return DeclInfo.Name + "." + GetAttrSwizzle(Index & 3); } } if (!Decls.TryGetValue(Index, out DeclInfo)) { - return null; + throw new InvalidOperationException(); } return DeclInfo.Name; } - private string GetBandExpr(ShaderIrOp Op) + private string GetAttrSwizzle(int Elem) { - return $"{GetInOperName(Op.OperandA)} && " + - $"{GetInOperName(Op.OperandB)}"; + return "xyzw".Substring(Elem, 1); } - private string GetBnotExpr(ShaderIrOp Op) + private string GetBandExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&&"); + private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!"); + + private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); + private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "=="); + private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<="); + private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">"); + private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!="); + private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">="); + + private string GetExitExpr(ShaderIrOp Op) => "return"; + + private string GetFabsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs"); + + private string GetFaddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+"); + + private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos"); + + private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2"); + + private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+"); + + private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2"); + + private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); + + private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); + + private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / "); + + private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt"); + + private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin"); + + private string GetIpaExpr(ShaderIrOp Op) => GetInOperName(Op.OperandA); + + private string GetKilExpr(ShaderIrOp Op) => "discard"; + + private string GetUnaryCall(ShaderIrOp Op, string FuncName) { - return $"!{GetInOperName(Op.OperandA)}"; + return FuncName + "(" + GetInOperName(Op.OperandA) + ")"; } - private string GetCltExpr(ShaderIrOp Op) + private string GetUnaryExpr(ShaderIrOp Op, string Opr) { - return $"{GetInOperName(Op.OperandA)} < " + - $"{GetInOperName(Op.OperandB)}"; + return Opr + GetInOperName(Op.OperandA); } - private string GetCeqExpr(ShaderIrOp Op) + private string GetBinaryExpr(ShaderIrOp Op, string Opr) { - return $"{GetInOperName(Op.OperandA)} == " + - $"{GetInOperName(Op.OperandB)}"; + return GetInOperName(Op.OperandA) + " " + Opr + " " + + GetInOperName(Op.OperandB); } - private string GetCleExpr(ShaderIrOp Op) + private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2) { - return $"{GetInOperName(Op.OperandA)} <= " + - $"{GetInOperName(Op.OperandB)}"; - } - - private string GetCgtExpr(ShaderIrOp Op) - { - return $"{GetInOperName(Op.OperandA)} > " + - $"{GetInOperName(Op.OperandB)}"; - } - - private string GetCneExpr(ShaderIrOp Op) - { - return $"{GetInOperName(Op.OperandA)} != " + - $"{GetInOperName(Op.OperandB)}"; - } - - private string GetCgeExpr(ShaderIrOp Op) - { - return $"{GetInOperName(Op.OperandA)} >= " + - $"{GetInOperName(Op.OperandB)}"; - } - - private string GetExitExpr(ShaderIrOp Op) - { - return "return"; - } - - private string GetFabsExpr(ShaderIrOp Op) - { - return $"abs({GetInOperName(Op.OperandA)})"; - } - - private string GetFaddExpr(ShaderIrOp Op) - { - return $"{GetInOperName(Op.OperandA)} + " + - $"{GetInOperName(Op.OperandB)}"; - } - - private string GetFcosExpr(ShaderIrOp Op) - { - return $"cos({GetInOperName(Op.OperandA)})"; - } - - private string GetFex2Expr(ShaderIrOp Op) - { - return $"exp2({GetInOperName(Op.OperandA)})"; - } - - private string GetFfmaExpr(ShaderIrOp Op) - { - return $"{GetInOperName(Op.OperandA)} * " + - $"{GetInOperName(Op.OperandB)} + " + - $"{GetInOperName(Op.OperandC)}"; - } - - private string GetFlg2Expr(ShaderIrOp Op) - { - return $"log2({GetInOperName(Op.OperandA)})"; - } - - private string GetFmulExpr(ShaderIrOp Op) - { - return $"{GetInOperName(Op.OperandA)} * " + - $"{GetInOperName(Op.OperandB)}"; - } - - private string GetFnegExpr(ShaderIrOp Op) - { - return $"-{GetInOperName(Op.OperandA)}"; - } - - private string GetFrcpExpr(ShaderIrOp Op) - { - return $"1 / {GetInOperName(Op.OperandA)}"; - } - - private string GetFrsqExpr(ShaderIrOp Op) - { - return $"inversesqrt({GetInOperName(Op.OperandA)})"; - } - - private string GetFsinExpr(ShaderIrOp Op) - { - return $"sin({GetInOperName(Op.OperandA)})"; - } - - private string GetIpaExpr(ShaderIrOp Op) - { - return GetInOperName(Op.OperandA); - } - - private string GetKilExpr(ShaderIrOp Op) - { - return "discard"; + return GetInOperName(Op.OperandA) + " " + Opr1 + " " + + GetInOperName(Op.OperandB) + " " + Opr2 + " " + + GetInOperName(Op.OperandC); } private string GetTexrExpr(ShaderIrOp Op) => GetTexExpr(Op, 'r'); @@ -666,7 +461,7 @@ namespace Ryujinx.Graphics.Gal.Shader int Handle = ((ShaderIrOperImm)Op.OperandC).Imm; - if (!Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo)) + if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo)) { throw new InvalidOperationException(); } @@ -676,40 +471,9 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetTexSamplerCoords(ShaderIrOp Op) { - if (GetInnerNode(Op.OperandA) is ShaderIrOperAbuf AAbuf && - GetInnerNode(Op.OperandB) is ShaderIrOperAbuf BAbuf) - { - if (AAbuf.GprIndex == ShaderIrOperGpr.ZRIndex && - BAbuf.GprIndex == ShaderIrOperGpr.ZRIndex && - (AAbuf.Offs >> 4) == (BAbuf.Offs >> 4)) - { - //Needs to call this to ensure it registers all elements used. - GetName(BAbuf); - - return $"{GetName(AAbuf, Swizzle: false)}." + - $"{GetAttrSwizzle((AAbuf.Offs >> 2) & 3)}" + - $"{GetAttrSwizzle((BAbuf.Offs >> 2) & 3)}"; - } - } - return "vec2(" + - $"{GetInOperName(Op.OperandA)}, " + - $"{GetInOperName(Op.OperandB)})"; - } - - private ShaderIrNode GetInnerNode(ShaderIrNode Node) - { - if (Node is ShaderIrOp Op && Op.Inst == ShaderIrInst.Ipa) - { - return Op.OperandA; - } - - return Node; - } - - private string GetAttrSwizzle(int Elem) - { - return "xyzw".Substring(Elem, 1); + GetInOperName(Op.OperandA) + ", " + + GetInOperName(Op.OperandB) + ")"; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs index 8a77694832..729b6f1ded 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs @@ -6,13 +6,13 @@ namespace Ryujinx.Graphics.Gal.Shader { public string Code { get; private set; } - public ICollection Textures { get; private set; } - public ICollection Uniforms { get; private set; } + public IEnumerable Textures { get; private set; } + public IEnumerable Uniforms { get; private set; } public GlslProgram( string Code, - ICollection Textures, - ICollection Uniforms) + IEnumerable Textures, + IEnumerable Uniforms) { this.Code = Code; this.Textures = Textures; diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs index 95d8e537f7..21b2ae70d5 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs @@ -41,6 +41,7 @@ namespace Ryujinx.Graphics.Gpu } AddMethod(0x585, 1, 1, VertexEndGl); + AddMethod(0x674, 1, 1, ClearBuffers); AddMethod(0x6c3, 1, 1, QueryControl); AddMethod(0x8e4, 16, 1, CbData); AddMethod(0x904, 1, 1, CbBind); @@ -66,18 +67,42 @@ namespace Ryujinx.Graphics.Gpu int TexHandle = ReadCb(Memory, TexCbuf, 0x20); + UploadShaders(Memory); + + Gpu.Renderer.BindProgram(); + + UploadUniforms(Memory); + UploadVertexArrays(Memory); + } + + private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry) + { + int Arg0 = PBEntry.Arguments[0]; + + int Rt = (Arg0 >> 6) & 0xf; + + GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); + + Gpu.Renderer.ClearBuffers(Rt, Flags); + } + + private void UploadShaders(AMemory Memory) + { long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); for (int Index = 0; Index < 6; Index++) { - int Offset = ReadRegister(NvGpuEngine3dReg.ShaderOffset + Index * 0x10); + int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10); + int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + Index * 0x10); - if (Offset == 0) + if (Offset == 0 || (Index != 1 && Index != 5)) { continue; } - long Position = Gpu.GetCpuAddr(BasePosition + (uint)Offset); + long Tag = BasePosition + (uint)Offset; + + long Position = Gpu.GetCpuAddr(Tag); if (Position == -1) { @@ -91,7 +116,25 @@ namespace Ryujinx.Graphics.Gpu GalShaderType ShaderType = GetTypeFromProgram(Index); - Gpu.Renderer.CreateShader(Position, Code, ShaderType); + Gpu.Renderer.CreateShader(Tag, ShaderType, Code); + Gpu.Renderer.BindShader(Tag); + } + } + + private void UploadUniforms(AMemory Memory) + { + long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); + + for (int Index = 0; Index < 5; Index++) + { + int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + (Index + 1) * 0x10); + + long Tag = BasePosition + (uint)Offset; + + if (Offset == 0 || (Index != 0 && Index != 4)) + { + continue; + } for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++) { @@ -99,16 +142,68 @@ namespace Ryujinx.Graphics.Gpu if (Cb.Enabled) { - long CbPosition = Cb.Position + (int)ShaderType * Cb.Size; + long CbPosition = Cb.Position + Index * Cb.Size; byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size); - Gpu.Renderer.SetShaderCb(Position, Cbuf, Data); + Gpu.Renderer.SetShaderCb(Tag, Cbuf, Data); } } } } + private void UploadVertexArrays(AMemory Memory) + { + List[] Attribs = new List[32]; + + for (int Attr = 0; Attr < 16; Attr++) + { + int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Attr); + + int ArrayIndex = Packed & 0x1f; + + if (Attribs[ArrayIndex] == null) + { + Attribs[ArrayIndex] = new List(); + } + + Attribs[ArrayIndex].Add(new GalVertexAttrib( + ((Packed >> 6) & 0x1) != 0, + (Packed >> 7) & 0x3fff, + (GalVertexAttribSize)((Packed >> 21) & 0x3f), + (GalVertexAttribType)((Packed >> 27) & 0x7), + ((Packed >> 31) & 0x1) != 0)); + } + + for (int Index = 0; Index < 32; Index++) + { + int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4); + + bool Enable = (Control & 0x1000) != 0; + + if (!Enable) + { + continue; + } + + long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); + long EndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4); + + long Size = (EndPos - Position) + 1; + + int Stride = Control & 0xfff; + + Position = Gpu.GetCpuAddr(Position); + + byte[] Data = AMemoryHelper.ReadBytes(Memory, Position, Size); + + GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0]; + + Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray); + Gpu.Renderer.RenderVertexArray(Index); + } + } + private static GalShaderType GetTypeFromProgram(int Program) { switch (Program) @@ -145,9 +240,9 @@ namespace Ryujinx.Graphics.Gpu private void CbData(AMemory Memory, NsGpuPBEntry PBEntry) { - if (TryGetCpuAddr(NvGpuEngine3dReg.CbAddress, out long Position)) + if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) { - int Offset = ReadRegister(NvGpuEngine3dReg.CbOffset); + int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferNOffset); foreach (int Arg in PBEntry.Arguments) { @@ -156,7 +251,7 @@ namespace Ryujinx.Graphics.Gpu Offset += 4; } - WriteRegister(NvGpuEngine3dReg.CbOffset, Offset); + WriteRegister(NvGpuEngine3dReg.ConstBufferNOffset, Offset); } } @@ -168,11 +263,11 @@ namespace Ryujinx.Graphics.Gpu Index = (Index >> 4) & 0x1f; - if (TryGetCpuAddr(NvGpuEngine3dReg.CbAddress, out long Position)) + if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) { Cbs[Index].Position = Position; Cbs[Index].Enabled = Enabled; - Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.CbSize); + Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize); } } diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs index 033e8a107f..ca6fd56dd6 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs @@ -2,17 +2,22 @@ namespace Ryujinx.Graphics.Gpu { enum NvGpuEngine3dReg { - ShaderAddress = 0x582, - QueryAddress = 0x6c0, - QuerySequence = 0x6c2, - QueryControl = 0x6c3, - ShaderControl = 0x800, - ShaderOffset = 0x801, - ShaderMaxGprs = 0x803, - ShaderType = 0x804, - CbSize = 0x8e0, - CbAddress = 0x8e1, - CbOffset = 0x8e3, - TextureCbIndex = 0x982 + VertexAttribNFormat = 0x458, + ShaderAddress = 0x582, + QueryAddress = 0x6c0, + QuerySequence = 0x6c2, + QueryControl = 0x6c3, + VertexArrayNControl = 0x700, + VertexArrayNAddress = 0x701, + VertexArrayNDivisor = 0x703, + VertexArrayNEndAddr = 0x7c0, + ShaderNControl = 0x800, + ShaderNOffset = 0x801, + ShaderNMaxGprs = 0x803, + ShaderNType = 0x804, + ConstBufferNSize = 0x8e0, + ConstBufferNAddress = 0x8e1, + ConstBufferNOffset = 0x8e3, + TextureCbIndex = 0x982 } } \ No newline at end of file diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 3bcef92183..d6a33edbfd 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -173,8 +173,6 @@ namespace Ryujinx Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " + $"{Ns.Statistics.GameFrameRate:0})"; - GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); - Renderer.RunActions(); Renderer.Render();