diff --git a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs index e314169b23..ca88b6377d 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs @@ -538,9 +538,9 @@ namespace Ryujinx.Core.OsHle.Services.Nv { byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size); - NsGpuPBEntry[] PushBuffer = NsGpuPBEntry.DecodePushBuffer(Data); + NsGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data); - Context.Ns.Gpu.ProcessPushBuffer(PushBuffer, Context.Memory); + Context.Ns.Gpu.Fifo.PushBuffer(Context.Memory, PushBuffer); } } diff --git a/Ryujinx.Graphics/Gal/GalShaderType.cs b/Ryujinx.Graphics/Gal/GalShaderType.cs new file mode 100644 index 0000000000..eb5aaf889a --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalShaderType.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalShaderType + { + Vertex = 0, + TessControl = 1, + TessEvaluation = 2, + Geometry = 3, + Fragment = 4 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index aa4eac4e14..9f3310d23b 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -21,6 +21,11 @@ namespace Ryujinx.Graphics.Gal float OffsY, float Rotate); + //Shader + void CreateShader(long Tag, byte[] Data, GalShaderType Type); + void SetShaderCb(int Cbuf, byte[] Data); + void BindShader(long Tag); + void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs); void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs new file mode 100644 index 0000000000..13114aff5d --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -0,0 +1,22 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + static class OGLEnumConverter + { + public static ShaderType GetShaderType(GalShaderType Type) + { + switch (Type) + { + case GalShaderType.Vertex: return ShaderType.VertexShader; + case GalShaderType.TessControl: return ShaderType.TessControlShader; + case GalShaderType.TessEvaluation: return ShaderType.TessEvaluationShader; + case GalShaderType.Geometry: return ShaderType.GeometryShader; + case GalShaderType.Fragment: return ShaderType.FragmentShader; + } + + throw new ArgumentException(nameof(Type)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs new file mode 100644 index 0000000000..4606489926 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OGLRasterizer + { + + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs new file mode 100644 index 0000000000..b2c94f4e23 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -0,0 +1,203 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Gal.Shader; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OGLShader + { + private class ShaderStage : IDisposable + { + public int Handle { get; private set; } + + public GalShaderType Type { get; private set; } + + public ShaderStage(GalShaderType Type) + { + Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); + + this.Type = Type; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing && Handle != 0) + { + GL.DeleteShader(Handle); + + Handle = 0; + } + } + } + + private struct ShaderProgram + { + public ShaderStage Vertex; + public ShaderStage TessControl; + public ShaderStage TessEvaluation; + public ShaderStage Geometry; + public ShaderStage Fragment; + } + + private ShaderProgram Current; + + private Dictionary Stages; + + private Dictionary Programs; + + public OGLShader() + { + Stages = new Dictionary(); + + Programs = new Dictionary(); + } + + public void Create(long Tag, byte[] Data, GalShaderType Type) + { + if (!Stages.ContainsKey(Tag)) + { + string Glsl = GetGlslCode(Data, Type); + + System.Console.WriteLine(Glsl); + + ShaderStage Stage = new ShaderStage(Type); + + Stages.Add(Tag, Stage); + + CompileAndCheck(Stage.Handle, Glsl); + } + } + + public void SetConstBuffer(int Cbuf, byte[] Data) + { + if (Cbuf != 3) return; + + Console.WriteLine("cb: " + Cbuf); + + foreach (byte b in Data) + { + Console.Write(b.ToString("x2") + " "); + } + + Console.WriteLine(); + } + + public void Bind(long Tag) + { + if (Stages.TryGetValue(Tag, out ShaderStage Stage)) + { + switch (Stage.Type) + { + case GalShaderType.Vertex: Current.Vertex = Stage; break; + case GalShaderType.TessControl: Current.TessControl = Stage; break; + case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break; + case GalShaderType.Geometry: Current.Geometry = Stage; break; + case GalShaderType.Fragment: Current.Fragment = Stage; break; + } + } + } + + public bool BindCurrentProgram() + { + if (Current.Vertex == null || + Current.Fragment == null) + { + return false; + } + + GL.UseProgram(GetCurrentProgramHandle()); + + return true; + } + + private int GetCurrentProgramHandle() + { + if (!Programs.TryGetValue(Current, out int Handle)) + { + Handle = GL.CreateProgram(); + + AttachIfNotNull(Handle, Current.Vertex); + AttachIfNotNull(Handle, Current.TessControl); + AttachIfNotNull(Handle, Current.TessEvaluation); + AttachIfNotNull(Handle, Current.Geometry); + AttachIfNotNull(Handle, Current.Fragment); + + GL.LinkProgram(Handle); + + CheckProgramLink(Handle); + + Programs.Add(Current, Handle); + } + + return Handle; + } + + private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage) + { + if (Stage != null) + { + GL.AttachShader(ProgramHandle, Stage.Handle); + } + } + + private string GetGlslCode(byte[] Data, GalShaderType Type) + { + int[] Code = new int[(Data.Length - 0x50) >> 2]; + + using (MemoryStream MS = new MemoryStream(Data)) + { + MS.Seek(0x50, SeekOrigin.Begin); + + BinaryReader Reader = new BinaryReader(MS); + + for (int Index = 0; Index < Code.Length; Index++) + { + Code[Index] = Reader.ReadInt32(); + } + } + + GlslDecompiler Decompiler = new GlslDecompiler(); + + return Decompiler.Decompile(Code, Type).Code; + } + + private static void CompileAndCheck(int Handle, string Code) + { + GL.ShaderSource(Handle, Code); + GL.CompileShader(Handle); + + CheckCompilation(Handle); + } + + private static void CheckCompilation(int Handle) + { + int Status = 0; + + GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status); + + if (Status == 0) + { + throw new ShaderException(GL.GetShaderInfoLog(Handle)); + } + } + + private static void CheckProgramLink(int Handle) + { + int Status = 0; + + GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out Status); + + if (Status == 0) + { + throw new ShaderException(GL.GetProgramInfoLog(Handle)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 002e54dadc..72661571b5 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -25,6 +25,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL private Texture[] Textures; + private OGLShader Shader; + private ConcurrentQueue ActionsQueue; private FrameBuffer FbRenderer; @@ -35,6 +37,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL Textures = new Texture[8]; + Shader = new OGLShader(); + ActionsQueue = new ConcurrentQueue(); } @@ -286,6 +290,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle); } + public void CreateShader(long Tag, byte[] Data, GalShaderType Type) + { + if (Data == null) + { + throw new ArgumentNullException(nameof(Data)); + } + + ActionsQueue.Enqueue(() => Shader.Create(Tag, Data, Type)); + } + + public void SetShaderCb(int Cbuf, byte[] Data) + { + if (Data == null) + { + throw new ArgumentNullException(nameof(Data)); + } + + ActionsQueue.Enqueue(() => Shader.SetConstBuffer(Cbuf, Data)); + } + + public void BindShader(long Tag) + { + ActionsQueue.Enqueue(() => Shader.Bind(Tag)); + } + private void EnsureVbInitialized(int VbIndex) { while (VbIndex >= VertexBuffers.Count) diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDeclInfo.cs b/Ryujinx.Graphics/Gal/Shader/GlslDeclInfo.cs new file mode 100644 index 0000000000..29da3dfcbc --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/GlslDeclInfo.cs @@ -0,0 +1,25 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + public class GlslDeclInfo + { + public string Name { get; private set; } + + public int Index { get; private set; } + public int Size { get; private set; } + + public GlslDeclInfo(string Name, int Index, int Size) + { + this.Name = Name; + this.Index = Index; + this.Size = Size; + } + + public void Enlarge(int NewSize) + { + if (Size < NewSize) + { + Size = NewSize; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index d65a33f5a1..42112c48fe 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace Ryujinx.Graphics.Gal.Shader @@ -14,20 +15,8 @@ namespace Ryujinx.Graphics.Gal.Shader private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; - private class Attrib - { - public string Name; - public int Elems; - - public Attrib(string Name, int Elems) - { - this.Name = Name; - this.Elems = Elems; - } - } - - private SortedDictionary InputAttributes; - private SortedDictionary OutputAttributes; + private SortedDictionary InputAttributes; + private SortedDictionary OutputAttributes; private HashSet UsedCbufs; @@ -82,23 +71,23 @@ namespace Ryujinx.Graphics.Gal.Shader }; } - public string Decompile(int[] Code, ShaderType Type) + public GlslProgram Decompile(int[] Code, GalShaderType Type) { - InputAttributes = new SortedDictionary(); - OutputAttributes = new SortedDictionary(); + InputAttributes = new SortedDictionary(); + OutputAttributes = new SortedDictionary(); UsedCbufs = new HashSet(); BodySB = new StringBuilder(); //FIXME: Only valid for vertex shaders. - if (Type == ShaderType.Fragment) + if (Type == GalShaderType.Fragment) { - OutputAttributes.Add(7, new Attrib("FragColor", 4)); + OutputAttributes.Add(7, new GlslDeclInfo("FragColor", -1, 4)); } else { - OutputAttributes.Add(7, new Attrib("gl_Position", 4)); + OutputAttributes.Add(7, new GlslDeclInfo("gl_Position", -1, 4)); } ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, Type); @@ -111,11 +100,11 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine("#version 430"); - PrintDeclSSBOs(SB); + PrintDeclUBOs(SB); PrintDeclInAttributes(SB); PrintDeclOutAttributes(SB); - if (Type == ShaderType.Fragment) + if (Type == GalShaderType.Fragment) { SB.AppendLine($"out vec4 {OutputAttributes[7].Name};"); SB.AppendLine(); @@ -129,16 +118,22 @@ namespace Ryujinx.Graphics.Gal.Shader BodySB.Clear(); - return SB.ToString(); + GlslProgram Program = new GlslProgram(); + + Program.Code = SB.ToString(); + + Program.Attributes = InputAttributes.Values.ToArray(); + + return Program; } - private void PrintDeclSSBOs(StringBuilder SB) + private void PrintDeclUBOs(StringBuilder SB) { foreach (int Cbuf in UsedCbufs) { - SB.AppendLine($"layout(std430, binding = {Cbuf}) buffer {CbufBuffPrefix}{Cbuf} {{"); + SB.AppendLine($"uniform _{CbufBuffPrefix}{Cbuf} {{"); SB.AppendLine($"{IdentationStr}float {CbufDataName}[];"); - SB.AppendLine("};"); + SB.AppendLine($"}} {CbufBuffPrefix}{Cbuf};"); SB.AppendLine(); } } @@ -147,15 +142,15 @@ namespace Ryujinx.Graphics.Gal.Shader { bool PrintNl = false; - foreach (KeyValuePair KV in InputAttributes) + foreach (KeyValuePair KV in InputAttributes) { int Index = KV.Key - AttrStartIndex; if (Index >= 0) { - string Type = ElemTypes[KV.Value.Elems]; + string Type = ElemTypes[KV.Value.Size]; - SB.AppendLine($"layout(location = {Index}) in {Type} {KV.Value.Name};"); + SB.AppendLine($"layout (location = {Index}) in {Type} {KV.Value.Name};"); PrintNl = true; } @@ -171,15 +166,15 @@ namespace Ryujinx.Graphics.Gal.Shader { bool PrintNl = false; - foreach (KeyValuePair KV in OutputAttributes) + foreach (KeyValuePair KV in OutputAttributes) { int Index = KV.Key - AttrStartIndex; if (Index >= 0) { - string Type = ElemTypes[KV.Value.Elems]; + string Type = ElemTypes[KV.Value.Size]; - SB.AppendLine($"layout(location = {Index}) out {Type} {KV.Value.Name};"); + SB.AppendLine($"layout (location = {Index}) out {Type} {KV.Value.Name};"); PrintNl = true; } @@ -357,44 +352,42 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetOutAbufName(ShaderIrOperAbuf Abuf) { - int AttrIndex = Abuf.Offs >> 4; + int Index = Abuf.Offs >> 4; int Elem = (Abuf.Offs >> 2) & 3; - if (!OutputAttributes.TryGetValue(AttrIndex, out Attrib Attr)) + if (!OutputAttributes.TryGetValue(Index, out GlslDeclInfo Attr)) { - Attr = new Attrib(OutputAttrPrefix + (AttrIndex - AttrStartIndex), Elem); + int GlslIndex = Index - AttrStartIndex; - OutputAttributes.Add(AttrIndex, Attr); + Attr = new GlslDeclInfo(OutputAttrPrefix + GlslIndex, GlslIndex, Elem); + + OutputAttributes.Add(Index, Attr); } - if (Attr.Elems < Elem) - { - Attr.Elems = Elem; - } + Attr.Enlarge(Elem); return $"{Attr.Name}.{GetAttrSwizzle(Elem)}"; } private string GetName(ShaderIrOperAbuf Abuf, bool Swizzle = true) { - int AttrIndex = Abuf.Offs >> 4; + int Index = Abuf.Offs >> 4; int Elem = (Abuf.Offs >> 2) & 3; - if (!InputAttributes.TryGetValue(AttrIndex, out Attrib Attr)) + if (!InputAttributes.TryGetValue(Index, out GlslDeclInfo Attr)) { - Attr = new Attrib(InputAttrPrefix + (AttrIndex - AttrStartIndex), Elem); + int GlslIndex = Index - AttrStartIndex; - InputAttributes.Add(AttrIndex, Attr); + Attr = new GlslDeclInfo(InputAttrPrefix + GlslIndex, GlslIndex, Elem); + + InputAttributes.Add(Index, Attr); } - if (Attr.Elems < Elem) - { - Attr.Elems = Elem; - } + Attr.Enlarge(Elem); - return Attr.Name + (Swizzle ? $".{GetAttrSwizzle(Elem)}" : string.Empty); + return Swizzle ? $"{Attr.Name}.{GetAttrSwizzle(Elem)}" : Attr.Name; } private string GetName(ShaderIrOperCbuf Cbuf) @@ -583,12 +576,10 @@ namespace Ryujinx.Graphics.Gal.Shader BAbuf.GprIndex == ShaderIrOperGpr.ZRIndex && (AAbuf.Offs >> 4) == (BAbuf.Offs >> 4)) { - string AttrName = GetName(AAbuf, Swizzle: false); - //Needs to call this to ensure it registers all elements used. GetName(BAbuf); - return $"{AttrName}." + + return $"{GetName(AAbuf, Swizzle: false)}." + $"{GetAttrSwizzle((AAbuf.Offs >> 2) & 3)}" + $"{GetAttrSwizzle((BAbuf.Offs >> 2) & 3)}"; } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs new file mode 100644 index 0000000000..4be9299cdf --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + struct GlslProgram + { + public string Code; + + public GlslDeclInfo[] ConstBuffers; + public GlslDeclInfo[] Attributes; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index 76d3a6c137..694d84a43e 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal.Shader { static class ShaderDecoder { - public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, ShaderType Type) + public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType Type) { ShaderIrBlock Block = new ShaderIrBlock(); @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Gal.Shader Decode(Block, OpCode); } - if (Type == ShaderType.Fragment) + if (Type == GalShaderType.Fragment) { Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x70, 0), new ShaderIrOperGpr(0))); Block.AddNode(new ShaderIrAsg(new ShaderIrOperAbuf(0x74, 0), new ShaderIrOperGpr(1))); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs deleted file mode 100644 index f218e4c335..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - public static class ShaderTest - { - public static void Test() - { - System.Collections.Generic.List CodeList = new System.Collections.Generic.List(); - - using (System.IO.FileStream FS = new System.IO.FileStream("D:\\puyo_fsh.bin", System.IO.FileMode.Open)) - { - System.IO.BinaryReader Reader = new System.IO.BinaryReader(FS); - - while (FS.Position + 8 <= FS.Length) - { - CodeList.Add(Reader.ReadInt32()); - } - } - - int[] Code = CodeList.ToArray(); - - GlslDecompiler Decompiler = new GlslDecompiler(); - - System.Console.WriteLine(Decompiler.Decompile(Code, ShaderType.Fragment)); - - System.Console.WriteLine("Done!"); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderType.cs b/Ryujinx.Graphics/Gal/Shader/ShaderType.cs deleted file mode 100644 index fc7d477e1f..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - enum ShaderType - { - Vertex, - Fragment - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/ShaderException.cs b/Ryujinx.Graphics/Gal/ShaderException.cs new file mode 100644 index 0000000000..9bc87ff3db --- /dev/null +++ b/Ryujinx.Graphics/Gal/ShaderException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.Graphics.Gal +{ + class ShaderException : Exception + { + public ShaderException() : base() { } + + public ShaderException(string Message) : base(Message) { } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/INvGpuEngine.cs b/Ryujinx.Graphics/Gpu/INvGpuEngine.cs new file mode 100644 index 0000000000..17e9b435c8 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/INvGpuEngine.cs @@ -0,0 +1,11 @@ +using ChocolArm64.Memory; + +namespace Ryujinx.Graphics.Gpu +{ + interface INvGpuEngine + { + int[] Registers { get; } + + void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/MacroInterpreter.cs b/Ryujinx.Graphics/Gpu/MacroInterpreter.cs new file mode 100644 index 0000000000..476d1a122c --- /dev/null +++ b/Ryujinx.Graphics/Gpu/MacroInterpreter.cs @@ -0,0 +1,313 @@ +using ChocolArm64.Memory; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu +{ + class MacroInterpreter + { + private INvGpuEngine Engine; + + public Queue Fifo { get; private set; } + + private int[] Gprs; + + private int MethAddr; + private int MethIncr; + + private bool Carry; + + private long Pc; + + public MacroInterpreter(INvGpuEngine Engine) + { + this.Engine = Engine; + + Fifo = new Queue(); + + Gprs = new int[8]; + } + + public void Execute(AMemory Memory, long Position, int Param) + { + Reset(); + + Pc = Position; + + Gprs[1] = Param; + + while (Step(Memory)); + } + + private void Reset() + { + for (int Index = 0; Index < Gprs.Length; Index++) + { + Gprs[Index] = 0; + } + + MethAddr = 0; + MethIncr = 0; + + Carry = false; + } + + private bool Step(AMemory Memory) + { + long BaseAddr = Pc; + + int OpCode = Memory.ReadInt32(Pc); + + Pc += 4; + + int Op = (OpCode >> 0) & 7; + + if (Op < 7) + { + //Operation produces a value. + int AsgOp = (OpCode >> 4) & 7; + + int Result = GetInstResult(OpCode); + + switch (AsgOp) + { + //Fetch parameter and ignore result. + case 0: SetDstGpr(OpCode, FetchParam()); break; + + //Move result. + case 1: SetDstGpr(OpCode, Result); break; + + //Move result and use as Method Address. + case 2: SetDstGpr(OpCode, Result); SetMethAddr(Result); break; + + //Fetch parameter and send result. + case 3: SetDstGpr(OpCode, FetchParam()); Send(Memory, Result); break; + + //Move and send result. + case 4: SetDstGpr(OpCode, Result); Send(Memory, Result); break; + + //Fetch parameter and use result as Method Address. + case 5: SetDstGpr(OpCode, FetchParam()); SetMethAddr(Result); break; + + //Move result and use as Method Address, then fetch and send paramter. + case 6: SetDstGpr(OpCode, Result); SetMethAddr(Result); Send(Memory, FetchParam()); break; + + //Move result and use as Method Address, then send bits 17:12 of result. + case 7: SetDstGpr(OpCode, Result); SetMethAddr(Result); Send(Memory, (Result >> 12) & 0x3f); break; + } + } + else + { + //Branch. + bool OnNotZero = ((OpCode >> 4) & 1) != 0; + + bool Taken = OnNotZero + ? GetGprA(OpCode) != 0 + : GetGprA(OpCode) == 0; + + if (Taken) + { + //Execute one more instruction due to delay slot. + bool KeepExecuting = Step(Memory); + + Pc = BaseAddr + (GetImm(OpCode) << 2); + + return KeepExecuting; + } + } + + if ((OpCode & 0x80) != 0) + { + //Exit (with a delay slot). + Step(Memory); + + return false; + } + + return true; + } + + private int GetInstResult(int OpCode) + { + int Low = OpCode & 7; + + switch (Low) + { + //Arithmetic or Logical operation. + case 0: + { + int AluOp = (OpCode >> 17) & 0x1f; + + return GetAluResult(AluOp, GetGprA(OpCode), GetGprB(OpCode)); + } + + //Add Immediate. + case 1: + { + return GetGprA(OpCode) + GetImm(OpCode); + } + + //Bitfield. + case 2: + case 3: + case 4: + { + int BfSrcBit = (OpCode >> 17) & 0x1f; + int BfSize = (OpCode >> 22) & 0x1f; + int BfDstBit = (OpCode >> 27) & 0x1f; + + int BfMask = (1 << BfSize) - 1; + + int Dst = GetGprA(OpCode); + int Src = GetGprB(OpCode); + + switch (Low) + { + //Bitfield move. + case 2: + { + Src = (Src >> BfSrcBit) & BfMask; + + Dst &= ~(BfMask << BfDstBit); + + Dst |= Src << BfDstBit; + + return Dst; + } + + //Bitfield extract with left shift by immediate. + case 3: + { + Src = (Src >> Dst) & BfMask; + + return Src << BfDstBit; + } + + //Bitfield extract with left shift by register. + case 4: + { + Src = (Src >> BfSrcBit) & BfMask; + + return Src << Dst; + } + } + + break; + } + + case 5: + { + return Read(GetGprA(OpCode) + GetImm(OpCode)); + } + } + + throw new ArgumentException(nameof(OpCode)); + } + + private int GetAluResult(int SubOp, int A, int B) + { + switch (SubOp) + { + //Add. + case 0: return A + B; + + //Add with Carry. + case 1: + { + ulong C = Carry ? 1UL : 0UL; + + ulong Result = (ulong)A + (ulong)B + C; + + Carry = Result > 0xffffffff; + + return (int)Result; + } + + //Subtract. + case 2: return A - B; + + //Subtract with Borrow. + case 3: + { + ulong C = Carry ? 0UL : 1UL; + + ulong Result = (ulong)A - (ulong)B - C; + + Carry = Result < 0x100000000; + + return (int)Result; + } + + //Exclusive Or. + case 8: return A ^ B; + + //Or. + case 9: return A | B; + + //And. + case 10: return A & B; + + //And Not. + case 11: return A & ~B; + + //Not And. + case 12: return ~(A & B); + } + + throw new ArgumentOutOfRangeException(nameof(SubOp)); + } + + private int GetImm(int OpCode) + { + //Note: The immediate is signed, the sign-extension is intended here. + return OpCode >> 14; + } + + private void SetMethAddr(int Value) + { + MethAddr = (Value >> 0) & 0xfff; + MethIncr = (Value >> 12) & 0x3f; + } + + private void SetDstGpr(int OpCode, int Value) + { + Gprs[(OpCode >> 8) & 7] = Value; + } + + private int GetGprA(int OpCode) + { + return GetGprValue((OpCode >> 11) & 7); + } + + private int GetGprB(int OpCode) + { + return GetGprValue((OpCode >> 14) & 7); + } + + private int GetGprValue(int Index) + { + return Index != 0 ? Gprs[Index] : 0; + } + + private int FetchParam() + { + Fifo.TryDequeue(out int Value); + + return Value; + } + + private int Read(int Reg) + { + return Engine.Registers[Reg]; + } + + private void Send(AMemory Memory, int Value) + { + NsGpuPBEntry PBEntry = new NsGpuPBEntry(MethAddr, 0, Value); + + Engine.CallMethod(Memory, PBEntry); + + MethAddr += MethIncr; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Graphics/Gpu/NsGpu.cs index 133d0af25b..5738050255 100644 --- a/Ryujinx.Graphics/Gpu/NsGpu.cs +++ b/Ryujinx.Graphics/Gpu/NsGpu.cs @@ -1,5 +1,5 @@ -using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; +using System.Threading; namespace Ryujinx.Graphics.Gpu { @@ -9,7 +9,13 @@ namespace Ryujinx.Graphics.Gpu internal NsGpuMemoryMgr MemoryMgr { get; private set; } - internal NsGpuPGraph PGraph { get; private set; } + public NvGpuFifo Fifo; + + internal NvGpuEngine3d Engine3d; + + private Thread FifoProcessing; + + private bool KeepRunning; public NsGpu(IGalRenderer Renderer) { @@ -17,7 +23,15 @@ namespace Ryujinx.Graphics.Gpu MemoryMgr = new NsGpuMemoryMgr(); - PGraph = new NsGpuPGraph(this); + Fifo = new NvGpuFifo(this); + + Engine3d = new NvGpuEngine3d(this); + + KeepRunning = true; + + FifoProcessing = new Thread(ProcessFifo); + + FifoProcessing.Start(); } public long GetCpuAddr(long Position) @@ -35,11 +49,6 @@ namespace Ryujinx.Graphics.Gpu return MemoryMgr.Map(CpuAddr, GpuAddr, Size); } - public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory) - { - PGraph.ProcessPushBuffer(PushBuffer, Memory); - } - public long ReserveMemory(long Size, long Align) { return MemoryMgr.Reserve(Size, Align); @@ -49,5 +58,15 @@ namespace Ryujinx.Graphics.Gpu { return MemoryMgr.Reserve(GpuAddr, Size, Align); } + + private void ProcessFifo() + { + while (KeepRunning) + { + Fifo.DispatchCalls(); + + Thread.Yield(); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs index 8063651aa8..d405a93c6a 100644 --- a/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs @@ -1,13 +1,11 @@ using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.IO; namespace Ryujinx.Graphics.Gpu { public struct NsGpuPBEntry { - public NsGpuRegister Register { get; private set; } + public int Method { get; private set; } public int SubChannel { get; private set; } @@ -15,65 +13,11 @@ namespace Ryujinx.Graphics.Gpu public ReadOnlyCollection Arguments => Array.AsReadOnly(m_Arguments); - public NsGpuPBEntry(NsGpuRegister Register, int SubChannel, params int[] Arguments) + public NsGpuPBEntry(int Method, int SubChannel, params int[] Arguments) { - this.Register = Register; + this.Method = Method; this.SubChannel = SubChannel; this.m_Arguments = Arguments; } - - public static NsGpuPBEntry[] DecodePushBuffer(byte[] Data) - { - using (MemoryStream MS = new MemoryStream(Data)) - { - BinaryReader Reader = new BinaryReader(MS); - - List GpFifos = new List(); - - bool CanRead() => MS.Position + 4 <= MS.Length; - - while (CanRead()) - { - int Packed = Reader.ReadInt32(); - - int Reg = (Packed << 2) & 0x7ffc; - int SubC = (Packed >> 13) & 7; - int Args = (Packed >> 16) & 0x1fff; - int Mode = (Packed >> 29) & 7; - - if (Mode == 4) - { - //Inline Mode. - GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Args)); - } - else - { - //Word mode. - if (Mode == 1) - { - //Sequential Mode. - for (int Index = 0; Index < Args && CanRead(); Index++, Reg += 4) - { - GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Reader.ReadInt32())); - } - } - else - { - //Non-Sequential Mode. - int[] Arguments = new int[Args]; - - for (int Index = 0; Index < Args && CanRead(); Index++) - { - Arguments[Index] = Reader.ReadInt32(); - } - - GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Arguments)); - } - } - } - - return GpFifos.ToArray(); - } - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs b/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs deleted file mode 100644 index 652f3e7517..0000000000 --- a/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs +++ /dev/null @@ -1,305 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Graphics.Gal; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gpu -{ - class NsGpuPGraph - { - private NsGpu Gpu; - - private uint[] Registers; - - public NsGpuEngine[] SubChannels; - - private Dictionary CurrentVertexBuffers; - - public NsGpuPGraph(NsGpu Gpu) - { - this.Gpu = Gpu; - - Registers = new uint[0x1000]; - - SubChannels = new NsGpuEngine[8]; - - CurrentVertexBuffers = new Dictionary(); - } - - public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory) - { - bool HasQuery = false; - - foreach (NsGpuPBEntry Entry in PushBuffer) - { - if (Entry.Arguments.Count == 1) - { - SetRegister(Entry.Register, (uint)Entry.Arguments[0]); - } - - switch (Entry.Register) - { - case NsGpuRegister.BindChannel: - if (Entry.Arguments.Count > 0) - { - SubChannels[Entry.SubChannel] = (NsGpuEngine)Entry.Arguments[0]; - } - break; - - case NsGpuRegister._3dVertexArray0Fetch: - SendVertexBuffers(Memory); - break; - - case NsGpuRegister._3dCbData0: - if (GetRegister(NsGpuRegister._3dCbPos) == 0x20) - { - SendTexture(Memory); - } - break; - - case NsGpuRegister._3dQueryAddressHigh: - case NsGpuRegister._3dQueryAddressLow: - case NsGpuRegister._3dQuerySequence: - case NsGpuRegister._3dQueryGet: - HasQuery = true; - break; - - case NsGpuRegister._3dSetShader: - uint ShaderPrg = (uint)Entry.Arguments[0]; - uint ShaderId = (uint)Entry.Arguments[1]; - uint CodeAddr = (uint)Entry.Arguments[2]; - uint ShaderType = (uint)Entry.Arguments[3]; - uint CodeEnd = (uint)Entry.Arguments[4]; - - SendShader( - Memory, - ShaderPrg, - ShaderId, - CodeAddr, - ShaderType, - CodeEnd); - break; - } - } - - if (HasQuery) - { - long Position = - (long)GetRegister(NsGpuRegister._3dQueryAddressHigh) << 32 | - (long)GetRegister(NsGpuRegister._3dQueryAddressLow) << 0; - - uint Seq = GetRegister(NsGpuRegister._3dQuerySequence); - uint Get = GetRegister(NsGpuRegister._3dQueryGet); - - uint Mode = Get & 3; - - if (Mode == 0) - { - //Write - Position = Gpu.MemoryMgr.GetCpuAddr(Position); - - if (Position != -1) - { - Gpu.Renderer.QueueAction(delegate() - { - Memory.WriteUInt32(Position, Seq); - }); - } - } - } - } - - private void SendVertexBuffers(AMemory Memory) - { - long Position = - (long)GetRegister(NsGpuRegister._3dVertexArray0StartHigh) << 32 | - (long)GetRegister(NsGpuRegister._3dVertexArray0StartLow) << 0; - - long Limit = - (long)GetRegister(NsGpuRegister._3dVertexArray0LimitHigh) << 32 | - (long)GetRegister(NsGpuRegister._3dVertexArray0LimitLow) << 0; - - int VbIndex = CurrentVertexBuffers.Count; - - if (!CurrentVertexBuffers.TryAdd(Position, VbIndex)) - { - VbIndex = CurrentVertexBuffers[Position]; - } - - if (Limit != 0) - { - long Size = (Limit - Position) + 1; - - Position = Gpu.MemoryMgr.GetCpuAddr(Position); - - if (Position != -1) - { - byte[] Buffer = AMemoryHelper.ReadBytes(Memory, Position, Size); - - int Stride = (int)GetRegister(NsGpuRegister._3dVertexArray0Fetch) & 0xfff; - - List Attribs = new List(); - - for (int Attr = 0; Attr < 16; Attr++) - { - int Packed = (int)GetRegister(NsGpuRegister._3dVertexAttrib0Format + Attr * 4); - - GalVertexAttrib Attrib = new GalVertexAttrib(Attr, - (Packed >> 0) & 0x1f, - ((Packed >> 6) & 0x1) != 0, - (Packed >> 7) & 0x3fff, - (GalVertexAttribSize)((Packed >> 21) & 0x3f), - (GalVertexAttribType)((Packed >> 27) & 0x7), - ((Packed >> 31) & 0x1) != 0); - - if (Attrib.Offset < Stride) - { - Attribs.Add(Attrib); - } - } - - Gpu.Renderer.QueueAction(delegate() - { - Gpu.Renderer.SendVertexBuffer(VbIndex, Buffer, Stride, Attribs.ToArray()); - }); - } - } - } - - private void SendTexture(AMemory Memory) - { - long TicPos = (long)GetRegister(NsGpuRegister._3dTicAddressHigh) << 32 | - (long)GetRegister(NsGpuRegister._3dTicAddressLow) << 0; - - uint CbData = GetRegister(NsGpuRegister._3dCbData0); - - uint TicIndex = (CbData >> 0) & 0xfffff; - uint TscIndex = (CbData >> 20) & 0xfff; //I guess? - - TicPos = Gpu.MemoryMgr.GetCpuAddr(TicPos + TicIndex * 0x20); - - if (TicPos != -1) - { - int Word0 = Memory.ReadInt32(TicPos + 0x0); - int Word1 = Memory.ReadInt32(TicPos + 0x4); - int Word2 = Memory.ReadInt32(TicPos + 0x8); - int Word3 = Memory.ReadInt32(TicPos + 0xc); - int Word4 = Memory.ReadInt32(TicPos + 0x10); - int Word5 = Memory.ReadInt32(TicPos + 0x14); - int Word6 = Memory.ReadInt32(TicPos + 0x18); - int Word7 = Memory.ReadInt32(TicPos + 0x1c); - - long TexAddress = Word1; - - TexAddress |= (long)(Word2 & 0xff) << 32; - - TexAddress = Gpu.MemoryMgr.GetCpuAddr(TexAddress); - - if (TexAddress != -1) - { - NsGpuTextureFormat Format = (NsGpuTextureFormat)(Word0 & 0x7f); - - int Width = (Word4 & 0xffff) + 1; - int Height = (Word5 & 0xffff) + 1; - - byte[] Buffer = GetDecodedTexture(Memory, Format, TexAddress, Width, Height); - - if (Buffer != null) - { - Gpu.Renderer.QueueAction(delegate() - { - Gpu.Renderer.SendR8G8B8A8Texture(0, Buffer, Width, Height); - }); - } - } - } - } - - private void SendShader( - AMemory Memory, - uint ShaderPrg, - uint ShaderId, - uint CodeAddr, - uint ShaderType, - uint CodeEnd) - { - long CodePos = Gpu.MemoryMgr.GetCpuAddr(CodeAddr); - - byte[] Data = AMemoryHelper.ReadBytes(Memory, CodePos, 0x300); - } - - private static byte[] GetDecodedTexture( - AMemory Memory, - NsGpuTextureFormat Format, - long Position, - int Width, - int Height) - { - byte[] Data = null; - - switch (Format) - { - case NsGpuTextureFormat.BC1: - { - int Size = (Width * Height) >> 1; - - Data = AMemoryHelper.ReadBytes(Memory, Position, Size); - - Data = BCn.DecodeBC1(new NsGpuTexture() - { - Width = Width, - Height = Height, - Data = Data - }, 0); - - break; - } - - case NsGpuTextureFormat.BC2: - { - int Size = Width * Height; - - Data = AMemoryHelper.ReadBytes(Memory, Position, Size); - - Data = BCn.DecodeBC2(new NsGpuTexture() - { - Width = Width, - Height = Height, - Data = Data - }, 0); - - break; - } - - case NsGpuTextureFormat.BC3: - { - int Size = Width * Height; - - Data = AMemoryHelper.ReadBytes(Memory, Position, Size); - - Data = BCn.DecodeBC3(new NsGpuTexture() - { - Width = Width, - Height = Height, - Data = Data - }, 0); - - break; - } - - //default: throw new NotImplementedException(Format.ToString()); - } - - return Data; - } - - public uint GetRegister(NsGpuRegister Register) - { - return Registers[((int)Register >> 2) & 0xfff]; - } - - public void SetRegister(NsGpuRegister Register, uint Value) - { - Registers[((int)Register >> 2) & 0xfff] = Value; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuRegister.cs b/Ryujinx.Graphics/Gpu/NsGpuRegister.cs deleted file mode 100644 index 4642e68d67..0000000000 --- a/Ryujinx.Graphics/Gpu/NsGpuRegister.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace Ryujinx.Graphics.Gpu -{ - public enum NsGpuRegister - { - BindChannel = 0, - - _2dClipEnable = 0x0290, - _2dOperation = 0x02ac, - - _3dGlobalBase = 0x02c8, - _3dRt0AddressHigh = 0x0800, - _3dRt0AddressLow = 0x0804, - _3dRt0Horiz = 0x0808, - _3dRt0Vert = 0x080c, - _3dRt0Format = 0x0810, - _3dRt0BlockDimensions = 0x0814, - _3dRt0ArrayMode = 0x0818, - _3dRt0LayerStride = 0x081c, - _3dRt0BaseLayer = 0x0820, - _3dViewportScaleX = 0x0a00, - _3dViewportScaleY = 0x0a04, - _3dViewportScaleZ = 0x0a08, - _3dViewportTranslateX = 0x0a0c, - _3dViewportTranslateY = 0x0a10, - _3dViewportTranslateZ = 0x0a14, - _3dViewportHoriz = 0x0c00, - _3dViewportVert = 0x0c04, - _3dDepthRangeNear = 0x0c08, - _3dDepthRangeFar = 0x0c0c, - _3dClearColorR = 0x0d80, - _3dClearColorG = 0x0d84, - _3dClearColorB = 0x0d88, - _3dClearColorA = 0x0d8c, - _3dScreenScissorHoriz = 0x0ff4, - _3dScreenScissorVert = 0x0ff8, - _3dVertexAttrib0Format = 0x1160, - _3dVertexAttrib1Format = 0x1164, - _3dVertexAttrib2Format = 0x1168, - _3dVertexAttrib3Format = 0x116c, - _3dVertexAttrib4Format = 0x1170, - _3dVertexAttrib5Format = 0x1174, - _3dVertexAttrib6Format = 0x1178, - _3dVertexAttrib7Format = 0x117c, - _3dVertexAttrib8Format = 0x1180, - _3dVertexAttrib9Format = 0x1184, - _3dVertexAttrib10Format = 0x1188, - _3dVertexAttrib11Format = 0x118c, - _3dVertexAttrib12Format = 0x1190, - _3dVertexAttrib13Format = 0x1194, - _3dVertexAttrib14Format = 0x1198, - _3dVertexAttrib15Format = 0x119c, - _3dScreenYControl = 0x13ac, - _3dTscAddressHigh = 0x155c, - _3dTscAddressLow = 0x1560, - _3dTscLimit = 0x1564, - _3dTicAddressHigh = 0x1574, - _3dTicAddressLow = 0x1578, - _3dTicLimit = 0x157c, - _3dMultiSampleMode = 0x15d0, - _3dVertexEndGl = 0x1614, - _3dVertexBeginGl = 0x1618, - _3dQueryAddressHigh = 0x1b00, - _3dQueryAddressLow = 0x1b04, - _3dQuerySequence = 0x1b08, - _3dQueryGet = 0x1b0c, - _3dVertexArray0Fetch = 0x1c00, - _3dVertexArray0StartHigh = 0x1c04, - _3dVertexArray0StartLow = 0x1c08, - _3dVertexArray1Fetch = 0x1c10, //todo: the rest - _3dVertexArray0LimitHigh = 0x1f00, - _3dVertexArray0LimitLow = 0x1f04, - _3dCbSize = 0x2380, - _3dCbAddressHigh = 0x2384, - _3dCbAddressLow = 0x2388, - _3dCbPos = 0x238c, - _3dCbData0 = 0x2390, - _3dCbData1 = 0x2394, - _3dCbData2 = 0x2398, - _3dCbData3 = 0x239c, - _3dCbData4 = 0x23a0, - _3dCbData5 = 0x23a4, - _3dCbData6 = 0x23a8, - _3dCbData7 = 0x23ac, - _3dCbData8 = 0x23b0, - _3dCbData9 = 0x23b4, - _3dCbData10 = 0x23b8, - _3dCbData11 = 0x23bc, - _3dCbData12 = 0x23c0, - _3dCbData13 = 0x23c4, - _3dCbData14 = 0x23c8, - _3dCbData15 = 0x23cc, - _3dSetShader = 0x3890 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpuEngine.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine.cs similarity index 61% rename from Ryujinx.Graphics/Gpu/NsGpuEngine.cs rename to Ryujinx.Graphics/Gpu/NvGpuEngine.cs index 118e2b72a5..624915d0d4 100644 --- a/Ryujinx.Graphics/Gpu/NsGpuEngine.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine.cs @@ -1,13 +1,11 @@ namespace Ryujinx.Graphics.Gpu { - enum NsGpuEngine + enum NvGpuEngine { - None = 0, _2d = 0x902d, _3d = 0xb197, Compute = 0xb1c0, Kepler = 0xa140, - Dma = 0xb0b5, - GpFifo = 0xb06f + Dma = 0xb0b5 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs new file mode 100644 index 0000000000..dd24d74e5a --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs @@ -0,0 +1,67 @@ +using ChocolArm64.Memory; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu +{ + class NvGpuEngine3d : INvGpuEngine + { + public int[] Registers { get; private set; } + + private NsGpu Gpu; + + private Dictionary Methods; + + public NvGpuEngine3d(NsGpu Gpu) + { + this.Gpu = Gpu; + + Registers = new int[0xe00]; + + Methods = new Dictionary() + { + { 0x6c3, QueryControl } + }; + } + + public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) + { + if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Methd)) + { + Methd(Memory, PBEntry); + } + + if (PBEntry.Arguments.Count == 1) + { + Registers[PBEntry.Method] = PBEntry.Arguments[0]; + } + } + + private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry) + { + long Position = MakeAddress(NvGpuEngine3dReg.QueryAddr); + + int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; + int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; + + int Mode = Ctrl & 3; + + if (Mode == 0) + { + //Write. + Position = Gpu.MemoryMgr.GetCpuAddr(Position); + + if (Position != -1) + { + Memory.WriteInt32(Position, Seq); + } + } + } + + private long MakeAddress(NvGpuEngine3dReg Reg) + { + return + (long)Registers[(int)Reg + 0] << 32 | + (uint)Registers[(int)Reg + 1]; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs new file mode 100644 index 0000000000..b029f406f8 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gpu +{ + enum NvGpuEngine3dReg + { + QueryAddr = 0x6c0, + QuerySequence = 0x6c2, + QueryControl = 0x6c3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs new file mode 100644 index 0000000000..53b2cf95e8 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs @@ -0,0 +1,177 @@ +using ChocolArm64.Memory; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu +{ + public class NvGpuFifo + { + private const int MacrosCount = 32; + private const int MacroIndexMask = MacrosCount - 1; + + private NsGpu Gpu; + + private ConcurrentQueue<(AMemory, NsGpuPBEntry[])> BufferQueue; + + private NvGpuEngine[] SubChannels; + + private struct CachedMacro + { + public long Position { get; private set; } + + private MacroInterpreter Interpreter; + + public CachedMacro(INvGpuEngine Engine, long Position) + { + this.Position = Position; + + Interpreter = new MacroInterpreter(Engine); + } + + public void PushParam(int Param) + { + Interpreter?.Fifo.Enqueue(Param); + } + + public void Execute(AMemory Memory, int Param) + { + Interpreter?.Execute(Memory, Position, Param); + } + } + + private long CurrentMacroPosition; + private int CurrentMacroBindIndex; + + private CachedMacro[] Macros; + + private Queue<(int, int)> MacroQueue; + + public NvGpuFifo(NsGpu Gpu) + { + this.Gpu = Gpu; + + BufferQueue = new ConcurrentQueue<(AMemory, NsGpuPBEntry[])>(); + + SubChannels = new NvGpuEngine[8]; + + Macros = new CachedMacro[MacrosCount]; + + MacroQueue = new Queue<(int, int)>(); + } + + public void PushBuffer(AMemory Memory, NsGpuPBEntry[] Buffer) + { + BufferQueue.Enqueue((Memory, Buffer)); + } + + public void DispatchCalls() + { + while (BufferQueue.TryDequeue(out (AMemory Memory, NsGpuPBEntry[] Buffer) Tuple)) + { + foreach (NsGpuPBEntry PBEntry in Tuple.Buffer) + { + CallMethod(Tuple.Memory, PBEntry); + } + + ExecuteMacros(Tuple.Memory); + } + } + + private void ExecuteMacros(AMemory Memory) + { + while (MacroQueue.TryDequeue(out (int Index, int Param) Tuple)) + { + Macros[Tuple.Index].Execute(Memory, Tuple.Param); + } + } + + private void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) + { + if (PBEntry.Method < 0x80) + { + switch ((NvGpuFifoMeth)PBEntry.Method) + { + case NvGpuFifoMeth.BindChannel: + { + NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0]; + + SubChannels[PBEntry.SubChannel] = Engine; + + break; + } + + case NvGpuFifoMeth.SetMacroUploadAddress: + { + CurrentMacroPosition = (long)((ulong)PBEntry.Arguments[0] << 2); + + break; + } + + case NvGpuFifoMeth.SendMacroCodeData: + { + long Position = Gpu.GetCpuAddr(CurrentMacroPosition); + + foreach (int Arg in PBEntry.Arguments) + { + Memory.WriteInt32(Position, Arg); + + CurrentMacroPosition += 4; + + Position += 4; + } + break; + } + + case NvGpuFifoMeth.SetMacroBindingIndex: + { + CurrentMacroBindIndex = PBEntry.Arguments[0]; + + break; + } + + case NvGpuFifoMeth.BindMacro: + { + long Position = (long)((ulong)PBEntry.Arguments[0] << 2); + + Position = Gpu.GetCpuAddr(Position); + + Macros[CurrentMacroBindIndex] = new CachedMacro(Gpu.Engine3d, Position); + + break; + } + } + } + else + { + switch (SubChannels[PBEntry.SubChannel]) + { + case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break; + } + } + } + + private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry) + { + if (PBEntry.Method < 0xe00) + { + Gpu.Engine3d.CallMethod(Memory, PBEntry); + } + else + { + int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask; + + if ((PBEntry.Method & 1) != 0) + { + foreach (int Arg in PBEntry.Arguments) + { + Macros[MacroIndex].PushParam(Arg); + } + } + else + { + MacroQueue.Enqueue((MacroIndex, PBEntry.Arguments[0])); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs b/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs new file mode 100644 index 0000000000..4287e25009 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gpu +{ + enum NvGpuFifoMeth + { + BindChannel = 0, + SetMacroUploadAddress = 0x45, + SendMacroCodeData = 0x46, + SetMacroBindingIndex = 0x47, + BindMacro = 0x48 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuMethod.cs b/Ryujinx.Graphics/Gpu/NvGpuMethod.cs new file mode 100644 index 0000000000..2923ddff0c --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NvGpuMethod.cs @@ -0,0 +1,6 @@ +using ChocolArm64.Memory; + +namespace Ryujinx.Graphics.Gpu +{ + delegate void NvGpuMethod(AMemory Memory, NsGpuPBEntry PBEntry); +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs b/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs new file mode 100644 index 0000000000..8cbb3288eb --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.Graphics.Gpu +{ + public static class NvGpuPushBuffer + { + private enum SubmissionMode + { + Incrementing = 1, + NonIncrementing = 3, + Immediate = 4, + IncrementOnce = 5 + } + + public static NsGpuPBEntry[] Decode(byte[] Data) + { + using (MemoryStream MS = new MemoryStream(Data)) + { + BinaryReader Reader = new BinaryReader(MS); + + List PushBuffer = new List(); + + bool CanRead() => MS.Position + 4 <= MS.Length; + + while (CanRead()) + { + int Packed = Reader.ReadInt32(); + + int Meth = (Packed >> 0) & 0x1fff; + int SubC = (Packed >> 13) & 7; + int Args = (Packed >> 16) & 0x1fff; + int Mode = (Packed >> 29) & 7; + + switch ((SubmissionMode)Mode) + { + case SubmissionMode.Incrementing: + { + for (int Index = 0; Index < Args && CanRead(); Index++, Meth++) + { + PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32())); + } + + break; + } + + case SubmissionMode.NonIncrementing: + { + int[] Arguments = new int[Args]; + + for (int Index = 0; Index < Arguments.Length; Index++) + { + if (!CanRead()) + { + break; + } + + Arguments[Index] = Reader.ReadInt32(); + } + + PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Arguments)); + + break; + } + + case SubmissionMode.Immediate: + { + PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Args)); + + break; + } + + case SubmissionMode.IncrementOnce: + { + if (CanRead()) + { + PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32())); + } + + if (CanRead() && Args > 1) + { + int[] Arguments = new int[Args - 1]; + + for (int Index = 0; Index < Arguments.Length && CanRead(); Index++) + { + Arguments[Index] = Reader.ReadInt32(); + } + + PushBuffer.Add(new NsGpuPBEntry(Meth + 1, SubC, Arguments)); + } + + break; + } + } + } + + return PushBuffer.ToArray(); + } + } + } +} \ No newline at end of file