diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs index 6a407baf03..d626622ab5 100644 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -1,6 +1,5 @@ using ChocolArm64.Translation; using System; -using System.Numerics; namespace ChocolArm64.Instruction { diff --git a/Ryujinx.Graphics/Gal/IGalBlend.cs b/Ryujinx.Graphics/Gal/IGalBlend.cs new file mode 100644 index 0000000000..5c96a49231 --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalBlend.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.Gal +{ + public interface IGalBlend + { + void Enable(); + + void Disable(); + + void Set( + GalBlendEquation Equation, + GalBlendFactor FuncSrc, + GalBlendFactor FuncDst); + + void SetSeparate( + GalBlendEquation EquationRgb, + GalBlendEquation EquationAlpha, + GalBlendFactor FuncSrcRgb, + GalBlendFactor FuncDstRgb, + GalBlendFactor FuncSrcAlpha, + GalBlendFactor FuncDstAlpha); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs new file mode 100644 index 0000000000..04e4cd8450 --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs @@ -0,0 +1,27 @@ +using System; + +namespace Ryujinx.Graphics.Gal +{ + public interface IGalFrameBuffer + { + void Create(long Tag, int Width, int Height); + + void Bind(long Tag); + + void BindTexture(long Tag, int Index); + + void Set(long Tag); + + void Set(byte[] Data, int Width, int Height); + + void SetTransform(float SX, float SY, float Rotate, float TX, float TY); + + void SetWindowSize(int Width, int Height); + + void SetViewport(int X, int Y, int Width, int Height); + + void Render(); + + void GetBufferData(long Tag, Action Callback); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs new file mode 100644 index 0000000000..7326fd0195 --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Graphics.Gal +{ + public interface IGalRasterizer + { + void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); + + bool IsVboCached(long Tag, long DataSize); + + bool IsIboCached(long Tag, long DataSize); + + void CreateVbo(long Tag, byte[] Buffer); + + void CreateIbo(long Tag, byte[] Buffer); + + void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs); + + void SetIndexArray(long Tag, int Size, GalIndexFormat Format); + + void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType); + + void DrawElements(long IboTag, int First, GalPrimitiveType PrimType); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index b8f83469bd..c6324c4a35 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -1,90 +1,21 @@ using System; -using System.Collections.Generic; namespace Ryujinx.Graphics.Gal { - public unsafe interface IGalRenderer + public interface IGalRenderer { void QueueAction(Action ActionMthd); void RunActions(); - void Render(); + IGalBlend Blend { get; } - void SetWindowSize(int Width, int Height); + IGalFrameBuffer FrameBuffer { get; } - //Blend - void SetBlendEnable(bool Enable); + IGalRasterizer Rasterizer { get; } - void SetBlend( - GalBlendEquation Equation, - GalBlendFactor FuncSrc, - GalBlendFactor FuncDst); + IGalShader Shader { get; } - void SetBlendSeparate( - GalBlendEquation EquationRgb, - GalBlendEquation EquationAlpha, - GalBlendFactor FuncSrcRgb, - GalBlendFactor FuncDstRgb, - GalBlendFactor FuncSrcAlpha, - GalBlendFactor FuncDstAlpha); - - //Frame Buffer - void CreateFrameBuffer(long Tag, int Width, int Height); - - void BindFrameBuffer(long Tag); - - void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler); - - void SetFrameBuffer(long Tag); - - void SetFrameBuffer(byte[] Data, int Width, int Height); - - void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY); - - void SetViewport(int X, int Y, int Width, int Height); - - void GetFrameBufferData(long Tag, Action Callback); - - //Rasterizer - void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); - - bool IsVboCached(long Tag, long DataSize); - - bool IsIboCached(long Tag, long DataSize); - - void CreateVbo(long Tag, byte[] Buffer); - - void CreateIbo(long Tag, byte[] Buffer); - - void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs); - - void SetIndexArray(long Tag, int Size, GalIndexFormat Format); - - void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType); - - void DrawElements(long IboTag, int First, GalPrimitiveType PrimType); - - //Shader - void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type); - - IEnumerable GetTextureUsage(long Tag); - - void SetConstBuffer(long Tag, int Cbuf, byte[] Data); - - void SetUniform1(string UniformName, int Value); - - void SetUniform2F(string UniformName, float X, float Y); - - void BindShader(long Tag); - - void BindProgram(); - - //Texture - void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler); - - bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture); - - void BindTexture(long Tag, int Index); + IGalTexture Texture { get; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs new file mode 100644 index 0000000000..e3f258aaaa --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalShader.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal +{ + public interface IGalShader + { + void Create(IGalMemory Memory, long Tag, GalShaderType Type); + + IEnumerable GetTextureUsage(long Tag); + + void SetConstBuffer(long Tag, int Cbuf, byte[] Data); + + void SetUniform1(string UniformName, int Value); + + void SetUniform2F(string UniformName, float X, float Y); + + void Bind(long Tag); + + void BindProgram(); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalTexture.cs b/Ryujinx.Graphics/Gal/IGalTexture.cs new file mode 100644 index 0000000000..d675854749 --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalTexture.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gal +{ + public interface IGalTexture + { + void Create(long Tag, byte[] Data, GalTexture Texture); + + bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture); + + void Bind(long Tag, int Index); + + void SetSampler(GalTextureSampler Sampler); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs index e33851e54a..7175e3a0ef 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs @@ -2,7 +2,7 @@ using OpenTK.Graphics.OpenGL; namespace Ryujinx.Graphics.Gal.OpenGL { - class OGLBlend + public class OGLBlend : IGalBlend { public void Enable() { diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs index 8f265f5442..7c7dee5114 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { - class OGLFrameBuffer + public class OGLFrameBuffer : IGalFrameBuffer { private struct Rect { @@ -16,9 +16,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL public Rect(int X, int Y, int Width, int Height) { - this.X = X; - this.Y = Y; - this.Width = Width; + this.X = X; + this.Y = Y; + this.Width = Width; this.Height = Height; } } @@ -185,10 +185,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL CurrTexHandle = RawFbTexHandle; } - public void SetTransform(Matrix2 Transform, Vector2 Offs) + public void SetTransform(float SX, float SY, float Rotate, float TX, float TY) { EnsureInitialized(); + Matrix2 Transform; + + Transform = Matrix2.CreateScale(SX, SY); + Transform *= Matrix2.CreateRotation(Rotate); + + Vector2 Offs = new Vector2(TX, TY); + int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); GL.UseProgram(Shader.Handle); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index b63c8b358c..7196161d22 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { - class OGLRasterizer + public class OGLRasterizer : IGalRasterizer { private static Dictionary AttribElements = new Dictionary() diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index be1bb20b45..8769cdd05a 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -7,7 +7,7 @@ using System.Linq; namespace Ryujinx.Graphics.Gal.OpenGL { - class OGLShader + public class OGLShader : IGalShader { private class ShaderStage : IDisposable { diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 56f157e575..a304cd8692 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.Gal.OpenGL { - class OGLTexture + public class OGLTexture : IGalTexture { private class TCE { @@ -173,7 +173,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - public static void Set(GalTextureSampler Sampler) + public void SetSampler(GalTextureSampler Sampler) { int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU); int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 4c4bd2caea..534e5daed8 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -1,21 +1,19 @@ -using OpenTK; using System; using System.Collections.Concurrent; -using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { public class OpenGLRenderer : IGalRenderer { - private OGLBlend Blend; + public IGalBlend Blend { get; private set; } - private OGLFrameBuffer FrameBuffer; + public IGalFrameBuffer FrameBuffer { get; private set; } - private OGLRasterizer Rasterizer; + public IGalRasterizer Rasterizer { get; private set; } - private OGLShader Shader; + public IGalShader Shader { get; private set; } - private OGLTexture Texture; + public IGalTexture Texture { get; private set; } private ConcurrentQueue ActionsQueue; @@ -48,237 +46,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL RenderAction(); } } - - public void Render() - { - FrameBuffer.Render(); - } - - public void SetWindowSize(int Width, int Height) - { - FrameBuffer.SetWindowSize(Width, Height); - } - - public void SetBlendEnable(bool Enable) - { - if (Enable) - { - ActionsQueue.Enqueue(() => Blend.Enable()); - } - else - { - ActionsQueue.Enqueue(() => Blend.Disable()); - } - } - - public void SetBlend( - GalBlendEquation Equation, - GalBlendFactor FuncSrc, - GalBlendFactor FuncDst) - { - ActionsQueue.Enqueue(() => Blend.Set(Equation, FuncSrc, FuncDst)); - } - - public void SetBlendSeparate( - GalBlendEquation EquationRgb, - GalBlendEquation EquationAlpha, - GalBlendFactor FuncSrcRgb, - GalBlendFactor FuncDstRgb, - GalBlendFactor FuncSrcAlpha, - GalBlendFactor FuncDstAlpha) - { - ActionsQueue.Enqueue(() => - { - Blend.SetSeparate( - EquationRgb, - EquationAlpha, - FuncSrcRgb, - FuncDstRgb, - FuncSrcAlpha, - FuncDstAlpha); - }); - } - - public void CreateFrameBuffer(long Tag, int Width, int Height) - { - ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height)); - } - - public void BindFrameBuffer(long Tag) - { - ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag)); - } - - public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler) - { - ActionsQueue.Enqueue(() => - { - FrameBuffer.BindTexture(Tag, Index); - - OGLTexture.Set(Sampler); - }); - } - - public void SetFrameBuffer(long Tag) - { - ActionsQueue.Enqueue(() => FrameBuffer.Set(Tag)); - } - - public void SetFrameBuffer(byte[] Data, int Width, int Height) - { - ActionsQueue.Enqueue(() => FrameBuffer.Set(Data, Width, Height)); - } - - public void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY) - { - Matrix2 Transform; - - Transform = Matrix2.CreateScale(SX, SY); - Transform *= Matrix2.CreateRotation(Rotate); - - Vector2 Offs = new Vector2(TX, TY); - - ActionsQueue.Enqueue(() => FrameBuffer.SetTransform(Transform, Offs)); - } - - public void SetViewport(int X, int Y, int Width, int Height) - { - ActionsQueue.Enqueue(() => FrameBuffer.SetViewport(X, Y, Width, Height)); - } - - public void GetFrameBufferData(long Tag, Action Callback) - { - ActionsQueue.Enqueue(() => FrameBuffer.GetBufferData(Tag, Callback)); - } - - public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) - { - ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags)); - } - - public bool IsVboCached(long Tag, long DataSize) - { - return Rasterizer.IsVboCached(Tag, DataSize); - } - - public bool IsIboCached(long Tag, long DataSize) - { - return Rasterizer.IsIboCached(Tag, DataSize); - } - - public void CreateVbo(long Tag, byte[] Buffer) - { - ActionsQueue.Enqueue(() => Rasterizer.CreateVbo(Tag, Buffer)); - } - - public void CreateIbo(long Tag, byte[] Buffer) - { - ActionsQueue.Enqueue(() => Rasterizer.CreateIbo(Tag, Buffer)); - } - - public void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs) - { - if ((uint)VbIndex > 31) - { - throw new ArgumentOutOfRangeException(nameof(VbIndex)); - } - - if (Attribs == null) - { - throw new ArgumentNullException(nameof(Attribs)); - } - - ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride, VboTag, Attribs)); - } - - public void SetIndexArray(long Tag, int Size, GalIndexFormat Format) - { - ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Tag, Size, Format)); - } - - public void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType) - { - ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(First, PrimCount, PrimType)); - } - - public void DrawElements(long IboTag, int First, GalPrimitiveType PrimType) - { - ActionsQueue.Enqueue(() => Rasterizer.DrawElements(IboTag, First, PrimType)); - } - - public void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type) - { - if (Memory == null) - { - throw new ArgumentNullException(nameof(Memory)); - } - - Shader.Create(Memory, Tag, Type); - } - - public void SetConstBuffer(long Tag, int Cbuf, byte[] Data) - { - if (Data == null) - { - throw new ArgumentNullException(nameof(Data)); - } - - ActionsQueue.Enqueue(() => Shader.SetConstBuffer(Tag, Cbuf, Data)); - } - - public void SetUniform1(string UniformName, int Value) - { - if (UniformName == null) - { - throw new ArgumentNullException(nameof(UniformName)); - } - - ActionsQueue.Enqueue(() => Shader.SetUniform1(UniformName, Value)); - } - - public void SetUniform2F(string UniformName, float X, float Y) - { - if (UniformName == null) - { - throw new ArgumentNullException(nameof(UniformName)); - } - - ActionsQueue.Enqueue(() => Shader.SetUniform2F(UniformName, X, Y)); - } - - public IEnumerable GetTextureUsage(long Tag) - { - return Shader.GetTextureUsage(Tag); - } - - public void BindShader(long Tag) - { - ActionsQueue.Enqueue(() => Shader.Bind(Tag)); - } - - public void BindProgram() - { - ActionsQueue.Enqueue(() => Shader.BindProgram()); - } - - public void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler) - { - ActionsQueue.Enqueue(() => - { - this.Texture.Create(Tag, Data, Texture); - - OGLTexture.Set(Sampler); - }); - } - - public bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture) - { - return this.Texture.TryGetCachedTexture(Tag, DataSize, out Texture); - } - - public void BindTexture(long Tag, int Index) - { - ActionsQueue.Enqueue(() => Texture.Bind(Tag, Index)); - } } } \ No newline at end of file diff --git a/Ryujinx.HLE/Gpu/GpuMacroException.cs b/Ryujinx.HLE/Gpu/GpuMacroException.cs new file mode 100644 index 0000000000..dffc065ec7 --- /dev/null +++ b/Ryujinx.HLE/Gpu/GpuMacroException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.HLE.Gpu +{ + class GpuMacroException : Exception + { + public GpuMacroException() : base() { } + + public GpuMacroException(string ExMsg) : base(ExMsg) { } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Gpu/GpuMacroExceptionMsgs.cs b/Ryujinx.HLE/Gpu/GpuMacroExceptionMsgs.cs new file mode 100644 index 0000000000..795b765d66 --- /dev/null +++ b/Ryujinx.HLE/Gpu/GpuMacroExceptionMsgs.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.Gpu +{ + static class GpuMacroExceptionMsgs + { + public const string CallCountExceeded = "Method call count exceeded the limit allowed per run!"; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Gpu/MacroInterpreter.cs b/Ryujinx.HLE/Gpu/MacroInterpreter.cs index c333046a98..4b866a427e 100644 --- a/Ryujinx.HLE/Gpu/MacroInterpreter.cs +++ b/Ryujinx.HLE/Gpu/MacroInterpreter.cs @@ -5,6 +5,10 @@ namespace Ryujinx.HLE.Gpu { class MacroInterpreter { + private const int MaxCallCountPerRun = 500; + + private int CallCount; + private enum AssignmentOperation { IgnoreAndFetch = 0, @@ -96,6 +100,8 @@ namespace Ryujinx.HLE.Gpu MethIncr = 0; Carry = false; + + CallCount = 0; } private bool Step(NvGpuVmm Vmm, int[] Mme) @@ -407,6 +413,15 @@ namespace Ryujinx.HLE.Gpu private void Send(NvGpuVmm Vmm, int Value) { + //This is an artificial limit that prevents excessive calls + //to VertexEndGl since that triggers rendering, and in the + //case that something is bugged and causes an absurd amount of + //draw calls, this prevents the system from freezing (and throws instead). + if (MethAddr == 0x585 && ++CallCount > MaxCallCountPerRun) + { + throw new GpuMacroException(GpuMacroExceptionMsgs.CallCountExceeded); + } + NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value); Engine.CallMethod(Vmm, PBEntry); diff --git a/Ryujinx.HLE/Gpu/NvGpu.cs b/Ryujinx.HLE/Gpu/NvGpu.cs index 0301fcf220..87079a33b8 100644 --- a/Ryujinx.HLE/Gpu/NvGpu.cs +++ b/Ryujinx.HLE/Gpu/NvGpu.cs @@ -13,10 +13,6 @@ namespace Ryujinx.HLE.Gpu public NvGpuEngine3d Engine3d { get; private set; } public NvGpuEngineDma EngineDma { get; private set; } - private Thread FifoProcessing; - - private bool KeepRunning; - public NvGpu(IGalRenderer Renderer) { this.Renderer = Renderer; @@ -26,22 +22,6 @@ namespace Ryujinx.HLE.Gpu Engine2d = new NvGpuEngine2d(this); Engine3d = new NvGpuEngine3d(this); EngineDma = new NvGpuEngineDma(this); - - KeepRunning = true; - - FifoProcessing = new Thread(ProcessFifo); - - FifoProcessing.Start(); - } - - private void ProcessFifo() - { - while (KeepRunning) - { - Fifo.DispatchCalls(); - - Thread.Yield(); - } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/Gpu/NvGpuEngine2d.cs b/Ryujinx.HLE/Gpu/NvGpuEngine2d.cs index 15667eeb23..53edebeb24 100644 --- a/Ryujinx.HLE/Gpu/NvGpuEngine2d.cs +++ b/Ryujinx.HLE/Gpu/NvGpuEngine2d.cs @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.Gpu SrcWidth = 1280; SrcHeight = 720; - Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) => + Gpu.Renderer.FrameBuffer.GetBufferData(Tag, (byte[] Buffer) => { CopyTexture( Vmm, diff --git a/Ryujinx.HLE/Gpu/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/NvGpuEngine3d.cs index 6d03e6b89d..5207263298 100644 --- a/Ryujinx.HLE/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/NvGpuEngine3d.cs @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.Gpu long[] Tags = UploadShaders(Vmm); - Gpu.Renderer.BindProgram(); + Gpu.Renderer.Shader.BindProgram(); SetAlphaBlending(); @@ -113,8 +113,8 @@ namespace Ryujinx.HLE.Gpu //Note: Using the Width/Height results seems to give incorrect results. //Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely. - Gpu.Renderer.CreateFrameBuffer(PA, 1280, 720); - Gpu.Renderer.BindFrameBuffer(PA); + Gpu.Renderer.FrameBuffer.Create(PA, 1280, 720); + Gpu.Renderer.FrameBuffer.Bind(PA); } private long[] UploadShaders(NvGpuVmm Vmm) @@ -142,8 +142,8 @@ namespace Ryujinx.HLE.Gpu Tags[(int)ShaderType] = Tag; - Gpu.Renderer.CreateShader(Vmm, Tag, ShaderType); - Gpu.Renderer.BindShader(Tag); + Gpu.Renderer.Shader.Create(Vmm, Tag, ShaderType); + Gpu.Renderer.Shader.Bind(Tag); } int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX); @@ -155,7 +155,7 @@ namespace Ryujinx.HLE.Gpu float SignX = MathF.Sign(SX); float SignY = MathF.Sign(SY); - Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY); + Gpu.Renderer.Shader.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY); return Tags; } @@ -180,7 +180,14 @@ namespace Ryujinx.HLE.Gpu //TODO: Support independent blend properly. bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; - Gpu.Renderer.SetBlendEnable(Enable); + if (Enable) + { + Gpu.Renderer.Blend.Enable(); + } + else + { + Gpu.Renderer.Blend.Disable(); + } if (!Enable) { @@ -203,7 +210,7 @@ namespace Ryujinx.HLE.Gpu GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); - Gpu.Renderer.SetBlendSeparate( + Gpu.Renderer.Blend.SetSeparate( EquationRgb, EquationAlpha, FuncSrcRgb, @@ -213,7 +220,7 @@ namespace Ryujinx.HLE.Gpu } else { - Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb); + Gpu.Renderer.Blend.Set(EquationRgb, FuncSrcRgb, FuncDstRgb); } } @@ -229,13 +236,13 @@ namespace Ryujinx.HLE.Gpu for (int Index = 0; Index < Tags.Length; Index++) { - foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index])) + foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetTextureUsage(Tags[Index])) { long Position = ConstBuffers[Index][TextureCbIndex].Position; UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index); - Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex); + Gpu.Renderer.Shader.SetUniform1(DeclInfo.Name, TexIndex); TexIndex++; } @@ -280,7 +287,7 @@ namespace Ryujinx.HLE.Gpu //we shouldn't read anything from memory and bind //the frame buffer texture instead, since we're not //really writing anything to memory. - Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler); + Gpu.Renderer.FrameBuffer.BindTexture(TextureAddress, TexIndex); } else { @@ -288,22 +295,29 @@ namespace Ryujinx.HLE.Gpu long Size = (uint)TextureHelper.GetTextureSize(NewTexture); - if (Gpu.Renderer.TryGetCachedTexture(Tag, Size, out GalTexture Texture)) + bool HasCachedTexture = false; + + if (Gpu.Renderer.Texture.TryGetCachedTexture(Tag, Size, out GalTexture Texture)) { if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Tag, Size, NvGpuBufferType.Texture)) { - Gpu.Renderer.BindTexture(Tag, TexIndex); + Gpu.Renderer.Texture.Bind(Tag, TexIndex); - return; + HasCachedTexture = true; } } - byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition); + if (!HasCachedTexture) + { + byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition); - Gpu.Renderer.SetTextureAndSampler(Tag, Data, NewTexture, Sampler); + Gpu.Renderer.Texture.Create(Tag, Data, NewTexture); + } - Gpu.Renderer.BindTexture(Tag, TexIndex); + Gpu.Renderer.Texture.Bind(Tag, TexIndex); } + + Gpu.Renderer.Texture.SetSampler(Sampler); } private void UploadUniforms(NvGpuVmm Vmm) @@ -331,7 +345,7 @@ namespace Ryujinx.HLE.Gpu { byte[] Data = Vmm.ReadBytes(Cb.Position, (uint)Cb.Size); - Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); + Gpu.Renderer.Shader.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); } } } @@ -341,33 +355,33 @@ namespace Ryujinx.HLE.Gpu { long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); - int IndexSize = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); - int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); - int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); + int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); + int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); + int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); - GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize; + GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt; - IndexSize = 1 << IndexSize; + int IndexEntrySize = 1 << IndexEntryFmt; - if (IndexSize > 4) + if (IndexEntrySize > 4) { throw new InvalidOperationException(); } if (IndexCount != 0) { - int IbSize = IndexCount * IndexSize; + int IbSize = IndexCount * IndexEntrySize; - bool IboCached = Gpu.Renderer.IsIboCached(IndexPosition, (uint)IbSize); + bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IndexPosition, (uint)IbSize); if (!IboCached || Vmm.IsRegionModified(IndexPosition, (uint)IbSize, NvGpuBufferType.Index)) { byte[] Data = Vmm.ReadBytes(IndexPosition, (uint)IbSize); - Gpu.Renderer.CreateIbo(IndexPosition, Data); + Gpu.Renderer.Rasterizer.CreateIbo(IndexPosition, Data); } - Gpu.Renderer.SetIndexArray(IndexPosition, IbSize, IndexFormat); + Gpu.Renderer.Rasterizer.SetIndexArray(IndexPosition, IbSize, IndexFormat); } List[] Attribs = new List[32]; @@ -429,27 +443,27 @@ namespace Ryujinx.HLE.Gpu VbSize = VertexCount * Stride; } - bool VboCached = Gpu.Renderer.IsVboCached(VertexPosition, VbSize); + bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VertexPosition, VbSize); if (!VboCached || Vmm.IsRegionModified(VertexPosition, VbSize, NvGpuBufferType.Vertex)) { byte[] Data = Vmm.ReadBytes(VertexPosition, VbSize); - Gpu.Renderer.CreateVbo(VertexPosition, Data); + Gpu.Renderer.Rasterizer.CreateVbo(VertexPosition, Data); } - Gpu.Renderer.SetVertexArray(Index, Stride, VertexPosition, Attribs[Index].ToArray()); + Gpu.Renderer.Rasterizer.SetVertexArray(Index, Stride, VertexPosition, Attribs[Index].ToArray()); } GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); if (IndexCount != 0) { - Gpu.Renderer.DrawElements(IndexPosition, IndexFirst, PrimType); + Gpu.Renderer.Rasterizer.DrawElements(IndexPosition, IndexFirst, PrimType); } else { - Gpu.Renderer.DrawArrays(VertexFirst, VertexCount, PrimType); + Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType); } } diff --git a/Ryujinx.HLE/Gpu/NvGpuFifo.cs b/Ryujinx.HLE/Gpu/NvGpuFifo.cs index 361c8bcec4..9e2fa68b35 100644 --- a/Ryujinx.HLE/Gpu/NvGpuFifo.cs +++ b/Ryujinx.HLE/Gpu/NvGpuFifo.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Threading; namespace Ryujinx.HLE.Gpu { @@ -11,6 +12,10 @@ namespace Ryujinx.HLE.Gpu //a guess here and use 256kb as the size. Increase if needed. private const int MmeWords = 256 * 256; + //This is used to prevent an unbounded growth of the FIFO queue. + //The game shouldn't send more commands than we are capable to process. + private const int FifoCapacity = 10000; + private NvGpu Gpu; private ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry)> BufferQueue; @@ -48,6 +53,8 @@ namespace Ryujinx.HLE.Gpu private int[] Mme; + private ManualResetEvent FifoWait; + public NvGpuFifo(NvGpu Gpu) { this.Gpu = Gpu; @@ -59,10 +66,19 @@ namespace Ryujinx.HLE.Gpu Macros = new CachedMacro[MacrosCount]; Mme = new int[MmeWords]; + + FifoWait = new ManualResetEvent(true); } public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer) { + if (BufferQueue.Count + Buffer.Length > FifoCapacity) + { + FifoWait.Reset(); + } + + FifoWait.WaitOne(); + foreach (NvGpuPBEntry PBEntry in Buffer) { BufferQueue.Enqueue((Vmm, PBEntry)); @@ -72,6 +88,8 @@ namespace Ryujinx.HLE.Gpu public void DispatchCalls() { while (Step()); + + FifoWait.Set(); } public bool Step() diff --git a/Ryujinx.HLE/Loaders/Npdm/ACI0.cs b/Ryujinx.HLE/Loaders/Npdm/ACI0.cs index 1f1b810ef6..47b30a3c2a 100644 --- a/Ryujinx.HLE/Loaders/Npdm/ACI0.cs +++ b/Ryujinx.HLE/Loaders/Npdm/ACI0.cs @@ -1,4 +1,3 @@ -using Ryujinx.HLE.OsHle.Utilities; using System; using System.IO; @@ -20,7 +19,7 @@ namespace Ryujinx.HLE.Loaders.Npdm public KernelAccessControl KernelAccessControl; public const long ACI0Magic = 'A' << 0 | 'C' << 8 | 'I' << 16 | '0' << 24; - + public ACI0(Stream ACI0Stream, int Offset) { ACI0Stream.Seek(Offset, SeekOrigin.Begin); diff --git a/Ryujinx.HLE/Loaders/Npdm/ACID.cs b/Ryujinx.HLE/Loaders/Npdm/ACID.cs index d0f0acdd5e..09768a9283 100644 --- a/Ryujinx.HLE/Loaders/Npdm/ACID.cs +++ b/Ryujinx.HLE/Loaders/Npdm/ACID.cs @@ -1,4 +1,3 @@ -using Ryujinx.HLE.OsHle.Utilities; using System; using System.IO; @@ -24,7 +23,7 @@ namespace Ryujinx.HLE.Loaders.Npdm public FSAccessControl FSAccessControl; public ServiceAccessControl ServiceAccessControl; public KernelAccessControl KernelAccessControl; - + public const long ACIDMagic = 'A' << 0 | 'C' << 8 | 'I' << 16 | 'D' << 24; public ACID(Stream ACIDStream, int Offset) diff --git a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs index d255e668cc..eaa662f03b 100644 --- a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs +++ b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs @@ -1,6 +1,4 @@ using Ryujinx.HLE.OsHle.Utilities; -using System; -using System.Collections.Generic; using System.IO; using System.Text; @@ -29,7 +27,7 @@ namespace Ryujinx.HLE.Loaders.Npdm public ACI0 ACI0; public ACID ACID; - + public const long NpdmMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24; public Npdm(Stream NPDMStream) @@ -61,7 +59,7 @@ namespace Ryujinx.HLE.Loaders.Npdm // ProcessCategory (0: regular title, 1: kernel built-in). Should be 0 here. ProcessCategory = EndianSwap.Swap32(Reader.ReadInt32()); - // Main entrypoint stack size + // Main entrypoint stack size // (Should(?) be page-aligned. In non-nspwn scenarios, values of 0 can also rarely break in Horizon. // This might be something auto-adapting or a security feature of some sort ?) MainEntrypointStackSize = Reader.ReadInt32(); diff --git a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs index fd33841a3d..ddd7d7ed2b 100644 --- a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs +++ b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; @@ -25,7 +24,7 @@ namespace Ryujinx.HLE.Loaders.Npdm int Length = ((ControlByte & 0x07)) + 1; bool RegisterAllowed = ((ControlByte & 0x80) != 0); - + Services.Add((Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed)); ByteReaded += Length + 1; diff --git a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs index b45dac6b39..a285d0b142 100644 --- a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs @@ -339,7 +339,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android Rotate = -MathF.PI * 0.5f; } - Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY); + Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY)); //TODO: Support double buffering here aswell, it is broken for GPU //frame buffers because it seems to be completely out of sync. @@ -347,7 +347,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android { //Frame buffer is rendered to by the GPU, we can just //bind the frame buffer texture, it's not necessary to read anything. - Renderer.SetFrameBuffer(FbAddr); + Renderer.QueueAction(() => Renderer.FrameBuffer.Set(FbAddr)); } else { @@ -357,7 +357,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android byte[] Data = TextureReader.Read(Context.Memory, Texture); - Renderer.SetFrameBuffer(Data, FbWidth, FbHeight); + Renderer.QueueAction(() => Renderer.FrameBuffer.Set(Data, FbWidth, FbHeight)); } Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index f75b24908f..f7b263cd0f 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -71,6 +71,11 @@ namespace Ryujinx.HLE Os.LoadProgram(FileName); } + public void ProcessFrame() + { + Gpu.Fifo.DispatchCalls(); + } + internal virtual void OnFinish(EventArgs e) { Finish?.Invoke(this, e); diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index e2024b5bbd..762e899fc5 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -42,7 +42,7 @@ namespace Ryujinx { VSync = VSyncMode.On; - Renderer.SetWindowSize(Width, Height); + Renderer.FrameBuffer.SetWindowSize(Width, Height); } protected override void OnUpdateFrame(FrameEventArgs e) @@ -183,24 +183,29 @@ namespace Ryujinx protected override void OnRenderFrame(FrameEventArgs e) { - Ns.Statistics.StartSystemFrame(); - - Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " + - $"{Ns.Statistics.GameFrameRate:0})"; - Renderer.RunActions(); - Renderer.Render(); + + Renderer.FrameBuffer.Render(); + + Ns.Statistics.EndSystemFrame(); + + double HostFps = Ns.Statistics.SystemFrameRate; + double GameFps = Ns.Statistics.GameFrameRate; + + Title = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0}"; SwapBuffers(); - Ns.Statistics.EndSystemFrame(); + Ns.Statistics.StartSystemFrame(); + + Ns.ProcessFrame(); Ns.Os.SignalVsync(); } protected override void OnResize(EventArgs e) { - Renderer.SetWindowSize(Width, Height); + Renderer.FrameBuffer.SetWindowSize(Width, Height); } protected override void OnKeyDown(KeyboardKeyEventArgs e)