From c8c86a3854fb3c96e65eca6b59d6058270a21a17 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 13 Apr 2018 15:12:58 -0300 Subject: [PATCH] Fix for current framebuffer issues (#78) [GPU] Fix some of the current framebuffer issues --- .../OsHle/Services/Nv/INvDrvServices.cs | 2 + Ryujinx.Core/OsHle/Services/Nv/NvMap.cs | 1 + Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 125 +++--- Ryujinx.Graphics/Gal/GalBlendEquation.cs | 10 +- Ryujinx.Graphics/Gal/GalBlendFactor.cs | 38 +- Ryujinx.Graphics/Gal/IGalRenderer.cs | 29 +- Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs | 250 ------------ .../Gal/OpenGL/OGLEnumConverter.cs | 61 ++- Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs | 207 ++++++++-- Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs | 20 +- Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs | 89 +++-- Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 2 +- .../Gal/Shader/ShaderDecodeMem.cs | 21 +- Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs | 4 +- Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs | 5 - .../Gal/Shader/ShaderOptExprProp.cs | 366 ------------------ Ryujinx.Graphics/Gpu/NsGpu.cs | 6 +- Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs | 103 +++-- Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs | 8 + Ryujinx.Graphics/Gpu/Texture.cs | 20 +- Ryujinx.Graphics/Gpu/TextureReader.cs | 2 +- Ryujinx.Graphics/Gpu/TextureSwizzle.cs | 2 +- Ryujinx/Ui/GLScreen.cs | 2 - 23 files changed, 482 insertions(+), 891 deletions(-) delete mode 100644 Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs delete mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs diff --git a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs index e41f03a43..abda5b862 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs @@ -244,6 +244,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv Context.Memory.WriteInt64(Position + 0x20, Offset); + Map.GpuAddress = Offset; + return 0; } diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs index 9ef703196..f3dd1f471 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs @@ -8,5 +8,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv public int Align; public int Kind; public long CpuAddress; + public long GpuAddress; } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 45b99ead1..4ab64e2ae 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -2,6 +2,7 @@ using ChocolArm64.Memory; using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Services.Nv; using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Gpu; using System; using System.Collections.Generic; using System.IO; @@ -63,13 +64,7 @@ namespace Ryujinx.Core.OsHle.Services.Android private ManualResetEvent WaitBufferFree; - private object RenderQueueLock; - - private int RenderQueueCount; - - private bool NvFlingerDisposed; - - private bool KeepRunning; + private bool Disposed; public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent) { @@ -92,10 +87,6 @@ namespace Ryujinx.Core.OsHle.Services.Android BufferQueue = new BufferEntry[0x40]; WaitBufferFree = new ManualResetEvent(false); - - RenderQueueLock = new object(); - - KeepRunning = true; } public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code) @@ -285,35 +276,24 @@ namespace Ryujinx.Core.OsHle.Services.Android return 0; } - private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot) + private void SendFrameBuffer(ServiceCtx Context, int Slot) { - int FbWidth = BufferQueue[Slot].Data.Width; - int FbHeight = BufferQueue[Slot].Data.Height; - - long FbSize = (uint)FbWidth * FbHeight * 4; + int FbWidth = 1280; + int FbHeight = 720; NvMap Map = GetNvMap(Context, Slot); NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0); - long Address = Map.CpuAddress; + long CpuAddr = Map.CpuAddress; + long GpuAddr = Map.GpuAddress; if (MapFb.HasBufferOffset(Slot)) { - Address += MapFb.GetBufferOffset(Slot); - } + CpuAddr += MapFb.GetBufferOffset(Slot); - if ((ulong)(Address + FbSize) > AMemoryMgr.AddrSize) - { - Logging.Error($"Frame buffer address {Address:x16} is invalid!"); - - BufferQueue[Slot].State = BufferState.Free; - - ReleaseEvent.Handle.Set(); - - WaitBufferFree.Set(); - - return; + //TODO: Enable once the frame buffers problems are fixed. + //GpuAddr += MapFb.GetBufferOffset(Slot); } BufferQueue[Slot].State = BufferState.Acquired; @@ -367,41 +347,28 @@ namespace Ryujinx.Core.OsHle.Services.Android Rotate = -MathF.PI * 0.5f; } - lock (RenderQueueLock) - { - if (NvFlingerDisposed) - { - return; - } + Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY); - Interlocked.Increment(ref RenderQueueCount); + //TODO: Support double buffering here aswell, it is broken for GPU + //frame buffers because it seems to be completely out of sync. + if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(GpuAddr)) + { + //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(GpuAddr); + } + else + { + //Frame buffer is not set on the GPU registers, in this case + //assume that the app is manually writing to it. + Texture Texture = new Texture(CpuAddr, FbWidth, FbHeight); + + byte[] Data = TextureReader.Read(Context.Memory, Texture); + + Renderer.SetFrameBuffer(Data, FbWidth, FbHeight); } - byte* Fb = (byte*)Context.Memory.Ram + Address; - - Context.Ns.Gpu.Renderer.QueueAction(delegate() - { - Context.Ns.Gpu.Renderer.SetFrameBuffer( - Fb, - FbWidth, - FbHeight, - ScaleX, - ScaleY, - OffsX, - OffsY, - Rotate); - - BufferQueue[Slot].State = BufferState.Free; - - Interlocked.Decrement(ref RenderQueueCount); - - ReleaseEvent.Handle.Set(); - - lock (WaitBufferFree) - { - WaitBufferFree.Set(); - } - }); + Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); } private NvMap GetNvMap(ServiceCtx Context, int Slot) @@ -420,6 +387,18 @@ namespace Ryujinx.Core.OsHle.Services.Android return INvDrvServices.NvMaps.GetData(Context.Process, NvMapHandle); } + private void ReleaseBuffer(int Slot) + { + BufferQueue[Slot].State = BufferState.Free; + + ReleaseEvent.Handle.Set(); + + lock (WaitBufferFree) + { + WaitBufferFree.Set(); + } + } + private int GetFreeSlotBlocking(int Width, int Height) { int Slot; @@ -435,7 +414,7 @@ namespace Ryujinx.Core.OsHle.Services.Android Logging.Debug("Waiting for a free BufferQueue slot..."); - if (!KeepRunning) + if (Disposed) { break; } @@ -445,7 +424,7 @@ namespace Ryujinx.Core.OsHle.Services.Android WaitBufferFree.WaitOne(); } - while (KeepRunning); + while (!Disposed); Logging.Debug($"Found free BufferQueue slot {Slot}!"); @@ -485,26 +464,12 @@ namespace Ryujinx.Core.OsHle.Services.Android protected virtual void Dispose(bool Disposing) { - if (Disposing && !NvFlingerDisposed) + if (Disposing && !Disposed) { - lock (RenderQueueLock) - { - NvFlingerDisposed = true; - } - - //Ensure that all pending actions was sent before - //we can safely assume that the class was disposed. - while (RenderQueueCount > 0) - { - Thread.Yield(); - } - - Renderer.ResetFrameBuffer(); + Disposed = true; lock (WaitBufferFree) { - KeepRunning = false; - WaitBufferFree.Set(); } diff --git a/Ryujinx.Graphics/Gal/GalBlendEquation.cs b/Ryujinx.Graphics/Gal/GalBlendEquation.cs index d9f8e7993..7fd4ba5fa 100644 --- a/Ryujinx.Graphics/Gal/GalBlendEquation.cs +++ b/Ryujinx.Graphics/Gal/GalBlendEquation.cs @@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Gal { public enum GalBlendEquation { - FuncAdd = 0x8006, - Min = 0x8007, - Max = 0x8008, - FuncSubtract = 0x800a, - FuncReverseSubtract = 0x800b + FuncAdd = 1, + FuncSubtract = 2, + FuncReverseSubtract = 3, + Min = 4, + Max = 5 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalBlendFactor.cs b/Ryujinx.Graphics/Gal/GalBlendFactor.cs index de7d45fd4..7237c4eda 100644 --- a/Ryujinx.Graphics/Gal/GalBlendFactor.cs +++ b/Ryujinx.Graphics/Gal/GalBlendFactor.cs @@ -2,24 +2,24 @@ namespace Ryujinx.Graphics.Gal { public enum GalBlendFactor { - Zero = 0x4000, - One = 0x4001, - SrcColor = 0x4300, - OneMinusSrcColor = 0x4301, - SrcAlpha = 0x4302, - OneMinusSrcAlpha = 0x4303, - DstAlpha = 0x4304, - OneMinusDstAlpha = 0x4305, - DstColor = 0x4306, - OneMinusDstColor = 0x4307, - SrcAlphaSaturate = 0x4308, - ConstantColor = 0xc001, - OneMinusConstantColor = 0xc002, - ConstantAlpha = 0xc003, - OneMinusConstantAlpha = 0xc004, - Src1Color = 0xc900, - OneMinusSrc1Color = 0xc901, - Src1Alpha = 0xc902, - OneMinusSrc1Alpha = 0xc903 + Zero = 0x1, + One = 0x2, + SrcColor = 0x3, + OneMinusSrcColor = 0x4, + SrcAlpha = 0x5, + OneMinusSrcAlpha = 0x6, + DstAlpha = 0x7, + OneMinusDstAlpha = 0x8, + DstColor = 0x9, + OneMinusDstColor = 0xa, + SrcAlphaSaturate = 0xb, + Src1Color = 0x10, + OneMinusSrc1Color = 0x11, + Src1Alpha = 0x12, + OneMinusSrc1Alpha = 0x13, + ConstantColor = 0x61, + OneMinusConstantColor = 0x62, + ConstantAlpha = 0x63, + OneMinusConstantAlpha = 0x64 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 99534672d..c30c79fb3 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -6,21 +6,12 @@ namespace Ryujinx.Graphics.Gal public unsafe interface IGalRenderer { void QueueAction(Action ActionMthd); + void RunActions(); - void InitializeFrameBuffer(); - void ResetFrameBuffer(); void Render(); + void SetWindowSize(int Width, int Height); - void SetFrameBuffer( - byte* Fb, - int Width, - int Height, - float ScaleX, - float ScaleY, - float OffsX, - float OffsY, - float Rotate); //Blend void SetBlendEnable(bool Enable); @@ -39,11 +30,17 @@ namespace Ryujinx.Graphics.Gal GalBlendFactor FuncDstAlpha); //Frame Buffer - void SetFb(int FbIndex, int Width, int Height); + void CreateFrameBuffer(long Tag, int Width, int Height); - void BindFrameBuffer(int FbIndex); + void BindFrameBuffer(long Tag); - void DrawFrameBuffer(int FbIndex); + 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); //Rasterizer void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); @@ -70,8 +67,8 @@ namespace Ryujinx.Graphics.Gal void BindProgram(); //Texture - void SetTexture(int Index, GalTexture Tex); + void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler); - void SetSampler(int Index, GalTextureSampler Sampler); + void BindTexture(int Index); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs deleted file mode 100644 index 7e7725d61..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs +++ /dev/null @@ -1,250 +0,0 @@ -using OpenTK; -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - unsafe class FrameBuffer - { - public int WindowWidth { get; set; } - public int WindowHeight { get; set; } - - private int VtxShaderHandle; - private int FragShaderHandle; - private int PrgShaderHandle; - - private int TexHandle; - private int TexWidth; - private int TexHeight; - - private int VaoHandle; - private int VboHandle; - - private int[] Pixels; - - private byte* FbPtr; - - private object FbPtrLock; - - public FrameBuffer(int Width, int Height) - { - if (Width < 0) - { - throw new ArgumentOutOfRangeException(nameof(Width)); - } - - if (Height < 0) - { - throw new ArgumentOutOfRangeException(nameof(Height)); - } - - FbPtrLock = new object(); - - TexWidth = Width; - TexHeight = Height; - - WindowWidth = Width; - WindowHeight = Height; - - SetupShaders(); - SetupTexture(); - SetupVertex(); - } - - private void SetupShaders() - { - VtxShaderHandle = GL.CreateShader(ShaderType.VertexShader); - FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader); - - string VtxShaderSource = EmbeddedResource.GetString("GlFbVtxShader"); - string FragShaderSource = EmbeddedResource.GetString("GlFbFragShader"); - - GL.ShaderSource(VtxShaderHandle, VtxShaderSource); - GL.ShaderSource(FragShaderHandle, FragShaderSource); - GL.CompileShader(VtxShaderHandle); - GL.CompileShader(FragShaderHandle); - - PrgShaderHandle = GL.CreateProgram(); - - GL.AttachShader(PrgShaderHandle, VtxShaderHandle); - GL.AttachShader(PrgShaderHandle, FragShaderHandle); - GL.LinkProgram(PrgShaderHandle); - GL.UseProgram(PrgShaderHandle); - - int TexUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "tex"); - - GL.Uniform1(TexUniformLocation, 0); - - int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size"); - - GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f)); - } - - private void SetupTexture() - { - Pixels = new int[TexWidth * TexHeight]; - - if (TexHandle == 0) - { - TexHandle = GL.GenTexture(); - } - - GL.BindTexture(TextureTarget.Texture2D, TexHandle); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - GL.TexImage2D(TextureTarget.Texture2D, - 0, - PixelInternalFormat.Rgba, - TexWidth, - TexHeight, - 0, - PixelFormat.Rgba, - PixelType.UnsignedByte, - IntPtr.Zero); - } - - private void SetupVertex() - { - VaoHandle = GL.GenVertexArray(); - VboHandle = GL.GenBuffer(); - - float[] Buffer = new float[] - { - -1, 1, 0, 0, - 1, 1, 1, 0, - -1, -1, 0, 1, - 1, -1, 1, 1 - }; - - IntPtr Length = new IntPtr(Buffer.Length * 4); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); - GL.BindBuffer(BufferTarget.ArrayBuffer, 0); - - GL.BindVertexArray(VaoHandle); - - GL.EnableVertexAttribArray(0); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0); - - GL.EnableVertexAttribArray(1); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8); - - GL.BindVertexArray(0); - } - - public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs) - { - if (Fb == null) - { - throw new ArgumentNullException(nameof(Fb)); - } - - if (Width < 0) - { - throw new ArgumentOutOfRangeException(nameof(Width)); - } - - if (Height < 0) - { - throw new ArgumentOutOfRangeException(nameof(Height)); - } - - lock (FbPtrLock) - { - FbPtr = Fb; - } - - if (Width != TexWidth || - Height != TexHeight) - { - TexWidth = Width; - TexHeight = Height; - - SetupTexture(); - } - - GL.UseProgram(PrgShaderHandle); - - int TransformUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "transform"); - - GL.UniformMatrix2(TransformUniformLocation, false, ref Transform); - - int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size"); - - GL.Uniform2(WindowSizeUniformLocation, new Vector2(WindowWidth, WindowHeight)); - - int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset"); - - GL.Uniform2(OffsetUniformLocation, Offs); - } - - public void Reset() - { - lock (FbPtrLock) - { - FbPtr = null; - } - } - - public void Render() - { - lock (FbPtrLock) - { - if (FbPtr == null) - { - return; - } - - for (int Y = 0; Y < TexHeight; Y++) - for (int X = 0; X < TexWidth; X++) - { - Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y))); - } - } - - GL.BindTexture(TextureTarget.Texture2D, TexHandle); - GL.TexSubImage2D(TextureTarget.Texture2D, - 0, - 0, - 0, - TexWidth, - TexHeight, - PixelFormat.Rgba, - PixelType.UnsignedByte, - Pixels); - - GL.ActiveTexture(TextureUnit.Texture0); - - GL.BindVertexArray(VaoHandle); - - GL.UseProgram(PrgShaderHandle); - - GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); - } - - private int GetSwizzleOffset(int X, int Y) - { - int Pos; - - Pos = (Y & 0x7f) >> 4; - Pos += (X >> 4) << 3; - Pos += (Y >> 7) * ((TexWidth >> 4) << 3); - Pos *= 1024; - Pos += ((Y & 0xf) >> 3) << 9; - Pos += ((X & 0xf) >> 3) << 8; - Pos += ((Y & 0x7) >> 1) << 6; - Pos += ((X & 0x7) >> 2) << 5; - Pos += ((Y & 0x1) >> 0) << 4; - Pos += ((X & 0x3) >> 0) << 2; - - return Pos; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 17bf62916..4cc0a0397 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -127,17 +127,72 @@ namespace Ryujinx.Graphics.Gal.OpenGL public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation) { - return (BlendEquationMode)BlendEquation; + switch (BlendEquation) + { + case GalBlendEquation.FuncAdd: return BlendEquationMode.FuncAdd; + case GalBlendEquation.FuncSubtract: return BlendEquationMode.FuncSubtract; + case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract; + case GalBlendEquation.Min: return BlendEquationMode.Min; + case GalBlendEquation.Max: return BlendEquationMode.Max; + } + + throw new ArgumentException(nameof(BlendEquation)); } public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor) { - return (BlendingFactorSrc)(BlendFactor - 0x4000); + switch (BlendFactor) + { + case GalBlendFactor.Zero: return BlendingFactorSrc.Zero; + case GalBlendFactor.One: return BlendingFactorSrc.One; + case GalBlendFactor.SrcColor: return BlendingFactorSrc.SrcColor; + case GalBlendFactor.OneMinusSrcColor: return BlendingFactorSrc.OneMinusSrcColor; + case GalBlendFactor.DstColor: return BlendingFactorSrc.DstColor; + case GalBlendFactor.OneMinusDstColor: return BlendingFactorSrc.OneMinusDstColor; + case GalBlendFactor.SrcAlpha: return BlendingFactorSrc.SrcAlpha; + case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactorSrc.OneMinusSrcAlpha; + case GalBlendFactor.DstAlpha: return BlendingFactorSrc.DstAlpha; + case GalBlendFactor.OneMinusDstAlpha: return BlendingFactorSrc.OneMinusDstAlpha; + case GalBlendFactor.ConstantColor: return BlendingFactorSrc.ConstantColor; + case GalBlendFactor.OneMinusConstantColor: return BlendingFactorSrc.OneMinusConstantColor; + case GalBlendFactor.ConstantAlpha: return BlendingFactorSrc.ConstantAlpha; + case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorSrc.OneMinusConstantAlpha; + case GalBlendFactor.SrcAlphaSaturate: return BlendingFactorSrc.SrcAlphaSaturate; + case GalBlendFactor.Src1Color: return BlendingFactorSrc.Src1Color; + case GalBlendFactor.OneMinusSrc1Color: return BlendingFactorSrc.OneMinusSrc1Color; + case GalBlendFactor.Src1Alpha: return BlendingFactorSrc.Src1Alpha; + case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorSrc.OneMinusSrc1Alpha; + } + + throw new ArgumentException(nameof(BlendFactor)); } public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor) { - return (BlendingFactorDest)(BlendFactor - 0x4000); + switch (BlendFactor) + { + case GalBlendFactor.Zero: return BlendingFactorDest.Zero; + case GalBlendFactor.One: return BlendingFactorDest.One; + case GalBlendFactor.SrcColor: return BlendingFactorDest.SrcColor; + case GalBlendFactor.OneMinusSrcColor: return BlendingFactorDest.OneMinusSrcColor; + case GalBlendFactor.DstColor: return BlendingFactorDest.DstColor; + case GalBlendFactor.OneMinusDstColor: return BlendingFactorDest.OneMinusDstColor; + case GalBlendFactor.SrcAlpha: return BlendingFactorDest.SrcAlpha; + case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactorDest.OneMinusSrcAlpha; + case GalBlendFactor.DstAlpha: return BlendingFactorDest.DstAlpha; + case GalBlendFactor.OneMinusDstAlpha: return BlendingFactorDest.OneMinusDstAlpha; + case GalBlendFactor.ConstantColor: return BlendingFactorDest.ConstantColor; + case GalBlendFactor.OneMinusConstantColor: return BlendingFactorDest.OneMinusConstantColor; + case GalBlendFactor.ConstantAlpha: return BlendingFactorDest.ConstantAlpha; + case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorDest.OneMinusConstantAlpha; + case GalBlendFactor.SrcAlphaSaturate: return BlendingFactorDest.SrcAlphaSaturate; + case GalBlendFactor.Src1Color: return BlendingFactorDest.Src1Color; + case GalBlendFactor.OneMinusSrc1Color: return BlendingFactorDest.OneMinusSrc1Color; + case GalBlendFactor.Src1Alpha: return BlendingFactorDest.Src1Alpha; + case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorDest.OneMinusSrc1Alpha; + } + + throw new ArgumentException(nameof(BlendFactor)); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs index f9c42ae01..818af3b3a 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -1,16 +1,30 @@ using OpenTK; using OpenTK.Graphics.OpenGL; using System; +using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { class OGLFrameBuffer { - private struct FrameBuffer + private class FrameBuffer { - public int FbHandle; - public int RbHandle; - public int TexHandle; + public int Width { get; set; } + public int Height { get; set; } + + public int Handle { get; private set; } + public int RbHandle { get; private set; } + public int TexHandle { get; private set; } + + public FrameBuffer(int Width, int Height) + { + this.Width = Width; + this.Height = Height; + + Handle = GL.GenFramebuffer(); + RbHandle = GL.GenRenderbuffer(); + TexHandle = GL.GenTexture(); + } } private struct ShaderProgram @@ -20,83 +34,175 @@ namespace Ryujinx.Graphics.Gal.OpenGL public int FpHandle; } - private FrameBuffer[] Fbs; + private Dictionary Fbs; private ShaderProgram Shader; private bool IsInitialized; + private int RawFbTexWidth; + private int RawFbTexHeight; + private int RawFbTexHandle; + + private int CurrFbHandle; + private int CurrTexHandle; + private int VaoHandle; private int VboHandle; public OGLFrameBuffer() { - Fbs = new FrameBuffer[16]; + Fbs = new Dictionary(); Shader = new ShaderProgram(); } - public void Set(int Index, int Width, int Height) + public void Create(long Tag, int Width, int Height) { - if (Fbs[Index].FbHandle != 0) + if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) { + if (Fb.Width != Width || + Fb.Height != Height) + { + SetupTexture(Fb.TexHandle, Width, Height); + + Fb.Width = Width; + Fb.Height = Height; + } + return; } - Fbs[Index].FbHandle = GL.GenFramebuffer(); - Fbs[Index].RbHandle = GL.GenRenderbuffer(); - Fbs[Index].TexHandle = GL.GenTexture(); + Fb = new FrameBuffer(Width, Height); - GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle); + SetupTexture(Fb.TexHandle, Width, Height); - GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); - GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, 1280, 720); + GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle); - GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle); + GL.RenderbufferStorage( + RenderbufferTarget.Renderbuffer, + RenderbufferStorage.Depth24Stencil8, + Width, + Height); - GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle); + GL.FramebufferRenderbuffer( + FramebufferTarget.Framebuffer, + FramebufferAttachment.DepthStencilAttachment, + RenderbufferTarget.Renderbuffer, + Fb.RbHandle); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); - - GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fbs[Index].TexHandle, 0); + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0, + Fb.TexHandle, + 0); GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + + Fbs.Add(Tag, Fb); } - public void Bind(int Index) + public void Bind(long Tag) { - if (Fbs[Index].FbHandle == 0) + if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) { - return; - } + GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); - GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle); + CurrFbHandle = Fb.Handle; + } } - public void Draw(int Index) + public void BindTexture(long Tag, int Index) { - if (Fbs[Index].FbHandle == 0) + if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) { - return; + GL.ActiveTexture(TextureUnit.Texture0 + Index); + + GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle); + } + } + + public void Set(long Tag) + { + if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) + { + CurrTexHandle = Fb.TexHandle; + } + } + + public void Set(byte[] Data, int Width, int Height) + { + if (RawFbTexHandle == 0) + { + RawFbTexHandle = GL.GenTexture(); } - EnsureInitialized(); + if (RawFbTexWidth != Width || + RawFbTexHeight != Height) + { + SetupTexture(RawFbTexHandle, Width, Height); - GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); - - GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle); + RawFbTexWidth = Width; + RawFbTexHeight = Height; + } GL.ActiveTexture(TextureUnit.Texture0); - GL.BindVertexArray(VaoHandle); + GL.BindTexture(TextureTarget.Texture2D, RawFbTexHandle); + + (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); + + GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data); + + CurrTexHandle = RawFbTexHandle; + } + + public void SetTransform(Matrix2 Transform, Vector2 Offs) + { + EnsureInitialized(); + + int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); GL.UseProgram(Shader.Handle); - GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); + int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform"); + + GL.UniformMatrix2(TransformUniformLocation, false, ref Transform); + + int OffsetUniformLocation = GL.GetUniformLocation(Shader.Handle, "offset"); + + GL.Uniform2(OffsetUniformLocation, ref Offs); + + GL.UseProgram(CurrentProgram); + } + + public void Render() + { + if (CurrTexHandle != 0) + { + EnsureInitialized(); + + GL.ActiveTexture(TextureUnit.Texture0); + + GL.BindTexture(TextureTarget.Texture2D, CurrTexHandle); + + int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); + + GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); + + GL.BindVertexArray(VaoHandle); + + GL.UseProgram(Shader.Handle); + + GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); + + //Restore the original state. + GL.BindFramebuffer(FramebufferTarget.Framebuffer, CurrFbHandle); + + GL.UseProgram(CurrentProgram); + } } private void EnsureInitialized() @@ -130,7 +236,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.LinkProgram(Shader.Handle); GL.UseProgram(Shader.Handle); - Matrix2 Transform = Matrix2.CreateScale(1, -1); + Matrix2 Transform = Matrix2.Identity; int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex"); @@ -178,5 +284,32 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8); } + + private void SetupTexture(int Handle, int Width, int Height) + { + GL.BindTexture(TextureTarget.Texture2D, Handle); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + + (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); + + const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba; + + const int Level = 0; + const int Border = 0; + + GL.TexImage2D( + TextureTarget.Texture2D, + Level, + InternalFmt, + Width, + Height, + Border, + Format, + Type, + IntPtr.Zero); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index b7c8999ee..9ea25056c 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -15,10 +15,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GL.ActiveTexture(TextureUnit.Texture0 + Index); - int Handle = EnsureTextureInitialized(Index); - - GL.BindTexture(TextureTarget.Texture2D, Handle); + Bind(Index); + const int Level = 0; //TODO: Support mipmap textures. const int Border = 0; if (IsCompressedTextureFormat(Texture.Format)) @@ -27,7 +26,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.CompressedTexImage2D( TextureTarget.Texture2D, - 0, + Level, InternalFmt, Texture.Width, Texture.Height, @@ -39,27 +38,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL { const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba; - (PixelFormat, PixelType) Format = OGLEnumConverter.GetTextureFormat(Texture.Format); + (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(Texture.Format); GL.TexImage2D( TextureTarget.Texture2D, - 0, + Level, InternalFmt, Texture.Width, Texture.Height, Border, - Format.Item1, - Format.Item2, + Format, + Type, Texture.Data); } } - public void Set(int Index, GalTextureSampler Sampler) + public void Bind(int Index) { int Handle = EnsureTextureInitialized(Index); GL.BindTexture(TextureTarget.Texture2D, Handle); + } + public static void Set(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 0b7bf92ac..b3ccae5f8 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -19,8 +19,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL private ConcurrentQueue ActionsQueue; - private FrameBuffer FbRenderer; - public OpenGLRenderer() { Blend = new OGLBlend(); @@ -36,16 +34,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue = new ConcurrentQueue(); } - public void InitializeFrameBuffer() - { - FbRenderer = new FrameBuffer(1280, 720); - } - - public void ResetFrameBuffer() - { - FbRenderer.Reset(); - } - public void QueueAction(Action ActionMthd) { ActionsQueue.Enqueue(ActionMthd); @@ -63,33 +51,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Render() { - FbRenderer.Render(); + FrameBuffer.Render(); } public void SetWindowSize(int Width, int Height) { - FbRenderer.WindowWidth = Width; - FbRenderer.WindowHeight = Height; - } - - public unsafe void SetFrameBuffer( - byte* Fb, - int Width, - int Height, - float ScaleX, - float ScaleY, - float OffsX, - float OffsY, - float Rotate) - { - Matrix2 Transform; - - Transform = Matrix2.CreateScale(ScaleX, ScaleY); - Transform *= Matrix2.CreateRotation(Rotate); - - Vector2 Offs = new Vector2(OffsX, OffsY); - - FbRenderer.Set(Fb, Width, Height, Transform, Offs); + //TODO } public void SetBlendEnable(bool Enable) @@ -132,19 +99,46 @@ namespace Ryujinx.Graphics.Gal.OpenGL }); } - public void SetFb(int FbIndex, int Width, int Height) + public void CreateFrameBuffer(long Tag, int Width, int Height) { - ActionsQueue.Enqueue(() => FrameBuffer.Set(FbIndex, Width, Height)); + ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height)); } - public void BindFrameBuffer(int FbIndex) + public void BindFrameBuffer(long Tag) { - ActionsQueue.Enqueue(() => FrameBuffer.Bind(FbIndex)); + ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag)); } - public void DrawFrameBuffer(int FbIndex) + public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler) { - ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex)); + 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 ClearBuffers(int RtIndex, GalClearBufferFlags Flags) @@ -239,14 +233,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => Shader.BindProgram()); } - public void SetTexture(int Index, GalTexture Tex) + public void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler) { - ActionsQueue.Enqueue(() => Texture.Set(Index, Tex)); + ActionsQueue.Enqueue(() => + { + this.Texture.Set(Index, Texture); + + OGLTexture.Set(Sampler); + }); } - public void SetSampler(int Index, GalTextureSampler Sampler) + public void BindTexture(int Index) { - ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler)); + ActionsQueue.Enqueue(() => Texture.Bind(Index)); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index e155e4753..82dbff9fa 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Gal.Shader public GlslProgram Decompile(int[] Code, GalShaderType ShaderType) { - ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType); + ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0); ShaderIrNode[] Nodes = Block.GetNodes(); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index 6553cfcfd..33f582316 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -74,11 +74,9 @@ namespace Ryujinx.Graphics.Gal.Shader for (int Ch = 0; Ch < 4; Ch++) { - ShaderIrOperGpr Dst = (Ch >> 1) != 0 - ? GetOperGpr28(OpCode) - : GetOperGpr0 (OpCode); - - Dst.Index += Ch & 1; + //Assign it to a temp because the destination registers + //may be used as texture coord input aswell. + ShaderIrOperGpr Dst = new ShaderIrOperGpr(0x100 + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); @@ -86,6 +84,19 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } + + for (int Ch = 0; Ch < 4; Ch++) + { + ShaderIrOperGpr Src = new ShaderIrOperGpr(0x100 + Ch); + + ShaderIrOperGpr Dst = (Ch >> 1) != 0 + ? GetOperGpr28(OpCode) + : GetOperGpr0 (OpCode); + + Dst.Index += Ch & 1; + + Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); + } } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index 7bebea625..e44d5b7d7 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, GalShaderType ShaderType) + public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset) { ShaderIrBlock Block = new ShaderIrBlock(); @@ -37,8 +37,6 @@ namespace Ryujinx.Graphics.Gal.Shader } } - Block.RunOptimizationPasses(ShaderType); - return Block; } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs index 1a96d3be9..c920d9fa5 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs @@ -16,11 +16,6 @@ namespace Ryujinx.Graphics.Gal.Shader Nodes.Add(Node); } - public void RunOptimizationPasses(GalShaderType ShaderType) - { - ShaderOptExprProp.Optimize(Nodes, ShaderType); - } - public ShaderIrNode[] GetNodes() { return Nodes.ToArray(); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs deleted file mode 100644 index 9ce7cbe31..000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs +++ /dev/null @@ -1,366 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.Shader -{ - static class ShaderOptExprProp - { - private struct UseSite - { - public ShaderIrNode Parent { get; private set; } - public ShaderIrCond Cond { get; private set; } - - public int UseIndex { get; private set; } - - public int OperIndex { get; private set; } - - public UseSite( - ShaderIrNode Parent, - ShaderIrCond Cond, - int UseIndex, - int OperIndex) - { - this.Parent = Parent; - this.Cond = Cond; - this.UseIndex = UseIndex; - this.OperIndex = OperIndex; - } - } - - private class RegUse - { - public ShaderIrAsg Asg { get; private set; } - - public int AsgIndex { get; private set; } - - public int LastSiteIndex { get; private set; } - - public ShaderIrCond Cond { get; private set; } - - private List Sites; - - public RegUse() - { - Sites = new List(); - } - - public void AddUseSite(UseSite Site) - { - if (LastSiteIndex < Site.UseIndex) - { - LastSiteIndex = Site.UseIndex; - } - - Sites.Add(Site); - } - - public bool TryPropagate() - { - //This happens when a untiliazied register is used, - //this usually indicates a decoding error, but may also - //be caused by bogus programs (?). In any case, we just - //keep the unitialized access and avoid trying to propagate - //the expression (since we can't propagate what doesn't yet exist). - if (Asg == null) - { - return false; - } - - if (Cond != null) - { - //If the assignment is conditional, we can only propagate - //to the use sites that shares the same condition of the assignment. - foreach (UseSite Site in Sites) - { - if (!IsSameCondition(Cond, Site.Cond)) - { - return false; - } - } - } - - if (Sites.Count > 0) - { - foreach (UseSite Site in Sites) - { - if (Site.Parent is ShaderIrCond Cond) - { - switch (Site.OperIndex) - { - case 0: Cond.Pred = Asg.Src; break; - case 1: Cond.Child = Asg.Src; break; - - default: throw new InvalidOperationException(); - } - } - else if (Site.Parent is ShaderIrOp Op) - { - switch (Site.OperIndex) - { - case 0: Op.OperandA = Asg.Src; break; - case 1: Op.OperandB = Asg.Src; break; - case 2: Op.OperandC = Asg.Src; break; - - default: throw new InvalidOperationException(); - } - } - else if (Site.Parent is ShaderIrAsg SiteAsg) - { - SiteAsg.Src = Asg.Src; - } - else - { - throw new InvalidOperationException(); - } - } - } - - return true; - } - - public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond) - { - this.Asg = Asg; - this.AsgIndex = AsgIndex; - this.Cond = Cond; - - LastSiteIndex = 0; - - Sites.Clear(); - } - } - - public static void Optimize(List Nodes, GalShaderType ShaderType) - { - Dictionary Uses = new Dictionary(); - - RegUse GetUse(int Key) - { - RegUse Use; - - if (!Uses.TryGetValue(Key, out Use)) - { - Use = new RegUse(); - - Uses.Add(Key, Use); - } - - return Use; - } - - int GetGprKey(int GprIndex) - { - return GprIndex; - } - - int GetPredKey(int PredIndex) - { - return PredIndex | 0x10000000; - } - - RegUse GetGprUse(int GprIndex) - { - return GetUse(GetGprKey(GprIndex)); - } - - RegUse GetPredUse(int PredIndex) - { - return GetUse(GetPredKey(PredIndex)); - } - - void RemoveUse(RegUse Use) - { - if (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg)) - { - throw new InvalidOperationException(); - } - } - - void FindRegUses( - List<(int, UseSite)> UseList, - ShaderIrNode Parent, - ShaderIrNode Node, - ShaderIrCond CondNode, - int UseIndex, - int OperIndex = 0) - { - if (Node is ShaderIrAsg Asg) - { - FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex); - } - else if (Node is ShaderIrCond Cond) - { - FindRegUses(UseList, Cond, Cond.Pred, CondNode, UseIndex, 0); - FindRegUses(UseList, Cond, Cond.Child, CondNode, UseIndex, 1); - } - else if (Node is ShaderIrOp Op) - { - FindRegUses(UseList, Op, Op.OperandA, CondNode, UseIndex, 0); - FindRegUses(UseList, Op, Op.OperandB, CondNode, UseIndex, 1); - FindRegUses(UseList, Op, Op.OperandC, CondNode, UseIndex, 2); - } - else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst) - { - UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex))); - } - else if (Node is ShaderIrOperPred Pred) - { - UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex))); - } - } - - void TryAddRegUseSite(ShaderIrNode Node, ShaderIrCond CondNode, int UseIndex) - { - List<(int, UseSite)> UseList = new List<(int, UseSite)>(); - - FindRegUses(UseList, null, Node, CondNode, UseIndex); - - foreach ((int Key, UseSite Site) in UseList) - { - GetUse(Key).AddUseSite(Site); - } - } - - bool TryPropagate(RegUse Use) - { - //We can only propagate if the registers that the expression depends - //on weren't assigned after the original expression assignment - //to a register took place. We traverse the expression tree to find - //all registers being used, if any of those registers was assigned - //after the assignment to be propagated, then we can't propagate. - if (Use?.Asg == null) - { - return false; - } - - List<(int, UseSite)> UseList = new List<(int, UseSite)>(); - - if (Use.Cond != null) - { - FindRegUses(UseList, null, Use.Cond, null, 0); - } - else - { - FindRegUses(UseList, Use.Asg, Use.Asg.Src, null, 0); - } - - foreach ((int Key, UseSite Site) in UseList) - { - //TODO: Build an assignment list inside RegUse, - //and check if there is an assignment inside the - //range of Use.AsgIndex and Use.LastSiteIndex, - //and if that's the case, then we should return false. - //The current method is too conservative. - if (GetUse(Key).AsgIndex >= Use.AsgIndex) - { - return false; - } - } - - return Use.TryPropagate(); - } - - for (int Index = 0, IterCount = 0; Index < Nodes.Count; Index++, IterCount++) - { - ShaderIrNode Node = Nodes[Index]; - - ShaderIrCond CondNode = null; - - if (Node is ShaderIrCond) - { - CondNode = (ShaderIrCond)Node; - } - - TryAddRegUseSite(Node, CondNode, IterCount);; - - while (Node is ShaderIrCond Cond) - { - Node = Cond.Child; - } - - if (!(Node is ShaderIrAsg Asg)) - { - continue; - } - - RegUse Use = null; - - if (Asg.Dst is ShaderIrOperGpr Gpr && !Gpr.IsConst) - { - Use = GetGprUse(Gpr.Index); - } - else if (Asg.Dst is ShaderIrOperPred Pred) - { - Use = GetPredUse(Pred.Index); - } - - bool CanRemoveAsg = CondNode == null; - - CanRemoveAsg |= IsSameCondition(CondNode, Use?.Cond); - - if (CanRemoveAsg && TryPropagate(Use)) - { - RemoveUse(Use); - - //Note: Only decrement if the removal was successful. - //RemoveUse throws when this is not the case so we should be good. - Index--; - } - - //All nodes inside conditional nodes can't be propagated, - //as we don't even know if they will be executed to begin with. - Use?.SetNewAsg(Asg, IterCount, CondNode); - } - - foreach (RegUse Use in Uses.Values) - { - //Gprs 0-3 are the color output on fragment shaders, - //so we can't remove the last assignments to those registers. - if (ShaderType == GalShaderType.Fragment) - { - if (Use.Asg?.Dst is ShaderIrOperGpr Gpr && Gpr.Index < 4) - { - continue; - } - } - - if (TryPropagate(Use)) - { - RemoveUse(Use); - } - } - } - - private static bool IsSameCondition(ShaderIrCond CondA, ShaderIrCond CondB) - { - if (CondA == null || CondB == null) - { - return CondA == CondB; - } - - if (CondA.Not != CondB.Not) - { - return false; - } - - if (CondA.Pred is ShaderIrOperPred PredA) - { - if (!(CondB.Pred is ShaderIrOperPred PredB)) - { - return false; - } - - if (PredA.Index != PredB.Index) - { - return false; - } - } - else if (CondA.Pred != CondB.Pred) - { - return false; - } - - return true; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Graphics/Gpu/NsGpu.cs index 573805025..9a2e90128 100644 --- a/Ryujinx.Graphics/Gpu/NsGpu.cs +++ b/Ryujinx.Graphics/Gpu/NsGpu.cs @@ -9,9 +9,9 @@ namespace Ryujinx.Graphics.Gpu internal NsGpuMemoryMgr MemoryMgr { get; private set; } - public NvGpuFifo Fifo; + public NvGpuFifo Fifo { get; private set; } - internal NvGpuEngine3d Engine3d; + public NvGpuEngine3d Engine3d { get; private set; } private Thread FifoProcessing; @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Gpu KeepRunning = true; - FifoProcessing = new Thread(ProcessFifo); + FifoProcessing = new Thread(ProcessFifo); FifoProcessing.Start(); } diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs index f4486f46c..1142e4aa6 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu { - class NvGpuEngine3d : INvGpuEngine + public class NvGpuEngine3d : INvGpuEngine { public int[] Registers { get; private set; } @@ -20,9 +20,9 @@ namespace Ryujinx.Graphics.Gpu public int Size; } - private ConstBuffer[] Cbs; + private ConstBuffer[] ConstBuffers; - private bool HasDataToRender; + private HashSet FrameBuffers; public NvGpuEngine3d(NsGpu Gpu) { @@ -48,7 +48,9 @@ namespace Ryujinx.Graphics.Gpu AddMethod(0x8e4, 16, 1, CbData); AddMethod(0x904, 1, 1, CbBind); - Cbs = new ConstBuffer[18]; + ConstBuffers = new ConstBuffer[18]; + + FrameBuffers = new HashSet(); } public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) @@ -76,19 +78,10 @@ namespace Ryujinx.Graphics.Gpu UploadTextures(Memory, Tags); UploadUniforms(Memory); UploadVertexArrays(Memory); - - HasDataToRender = true; } private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry) { - if (HasDataToRender) - { - HasDataToRender = false; - - Gpu.Renderer.DrawFrameBuffer(0); - } - int Arg0 = PBEntry.Arguments[0]; int FbIndex = (Arg0 >> 6) & 0xf; @@ -99,16 +92,23 @@ namespace Ryujinx.Graphics.Gpu SetFrameBuffer(0); - Gpu.Renderer.ClearBuffers(Layer, Flags); + //TODO: Enable this once the frame buffer problems are fixed. + //Gpu.Renderer.ClearBuffers(Layer, Flags); } private void SetFrameBuffer(int FbIndex) { - int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); - int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); + long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); - Gpu.Renderer.SetFb(FbIndex, Width, Height); - Gpu.Renderer.BindFrameBuffer(FbIndex); + FrameBuffers.Add(Address); + + int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); + int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); + + //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(Address, 1280, 720); + Gpu.Renderer.BindFrameBuffer(Address); } private long[] UploadShaders(AMemory Memory) @@ -167,23 +167,24 @@ namespace Ryujinx.Graphics.Gpu private void SetAlphaBlending() { - bool BlendEnableMaster = (ReadRegister(NvGpuEngine3dReg.BlendEnableMaster) & 1) != 0; + //TODO: Support independent blend properly. + bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendEnable) & 1) != 0; - Gpu.Renderer.SetBlendEnable(BlendEnableMaster); + Gpu.Renderer.SetBlendEnable(Enable); - bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.BlendSeparateAlpha) & 1) != 0; + bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0; - GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationRgb); + GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb); - GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcRgb); - GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstRgb); + GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb); + GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb); if (BlendSeparateAlpha) { - GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationAlpha); + GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha); - GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcAlpha); - GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstAlpha); + GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); + GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); Gpu.Renderer.SetBlendSeparate( EquationRgb, @@ -205,11 +206,13 @@ namespace Ryujinx.Graphics.Gpu int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); - long BasePosition = Cbs[TextureCbIndex].Position; + long BasePosition = ConstBuffers[TextureCbIndex].Position; - long Size = (uint)Cbs[TextureCbIndex].Size; + long Size = (uint)ConstBuffers[TextureCbIndex].Size; - int TexIndex = 0; + //Note: On the emulator renderer, Texture Unit 0 is + //reserved for drawing the frame buffer. + int TexIndex = 1; for (int Index = 0; Index < Tags.Length; Index++) { @@ -241,8 +244,25 @@ namespace Ryujinx.Graphics.Gpu TicPosition += TicIndex * 0x20; TscPosition += TscIndex * 0x20; - Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition)); - Gpu.Renderer.SetSampler(TexIndex, TextureFactory.MakeSampler(Gpu, Memory, TscPosition)); + GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition); + + long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff; + + if (FrameBuffers.Contains(TextureAddress)) + { + //This texture is a frame buffer texture, + //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); + } + else + { + GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition); + + Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler); + Gpu.Renderer.BindTexture(TexIndex); + } } private void UploadUniforms(AMemory Memory) @@ -262,9 +282,9 @@ namespace Ryujinx.Graphics.Gpu continue; } - for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++) + for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++) { - ConstBuffer Cb = Cbs[Cbuf]; + ConstBuffer Cb = ConstBuffers[Cbuf]; if (Cb.Enabled) { @@ -379,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu if (Mode == 0) { - //Write. + //Write mode. Memory.WriteInt32(Position, Seq); } } @@ -414,16 +434,16 @@ namespace Ryujinx.Graphics.Gpu if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) { - Cbs[Index].Position = Position; - Cbs[Index].Enabled = Enabled; + ConstBuffers[Index].Position = Position; + ConstBuffers[Index].Enabled = Enabled; - Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize); + ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize); } } private int ReadCb(AMemory Memory, int Cbuf, int Offset) { - long Position = Cbs[Cbuf].Position; + long Position = ConstBuffers[Cbuf].Position; int Value = Memory.ReadInt32(Position + Offset); @@ -465,5 +485,10 @@ namespace Ryujinx.Graphics.Gpu { Registers[(int)Reg] = Value; } + + public bool IsFrameBufferPosition(long Position) + { + return FrameBuffers.Contains(Position); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs index 4bba9abe0..cb0b9d983 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu FrameBufferNHeight = 0x203, FrameBufferNFormat = 0x204, VertexAttribNFormat = 0x458, + IBlendEnable = 0x4b9, BlendSeparateAlpha = 0x4cf, BlendEquationRgb = 0x4d0, BlendFuncSrcRgb = 0x4d1, @@ -31,6 +32,13 @@ namespace Ryujinx.Graphics.Gpu VertexArrayNControl = 0x700, VertexArrayNAddress = 0x701, VertexArrayNDivisor = 0x703, + IBlendNSeparateAlpha = 0x780, + IBlendNEquationRgb = 0x781, + IBlendNFuncSrcRgb = 0x782, + IBlendNFuncDstRgb = 0x783, + IBlendNEquationAlpha = 0x784, + IBlendNFuncSrcAlpha = 0x785, + IBlendNFuncDstAlpha = 0x786, VertexArrayNEndAddr = 0x7c0, ShaderNControl = 0x800, ShaderNOffset = 0x801, diff --git a/Ryujinx.Graphics/Gpu/Texture.cs b/Ryujinx.Graphics/Gpu/Texture.cs index 831c664d4..cbfa683dc 100644 --- a/Ryujinx.Graphics/Gpu/Texture.cs +++ b/Ryujinx.Graphics/Gpu/Texture.cs @@ -2,7 +2,7 @@ using Ryujinx.Graphics.Gal; namespace Ryujinx.Graphics.Gpu { - struct Texture + public struct Texture { public long Position { get; private set; } @@ -16,6 +16,24 @@ namespace Ryujinx.Graphics.Gpu public GalTextureFormat Format { get; private set; } + public Texture( + long Position, + int Width, + int Height) + { + this.Position = Position; + this.Width = Width; + this.Height = Height; + + Pitch = 0; + + BlockHeight = 16; + + Swizzle = TextureSwizzle.BlockLinear; + + Format = GalTextureFormat.A8B8G8R8; + } + public Texture( long Position, int Width, diff --git a/Ryujinx.Graphics/Gpu/TextureReader.cs b/Ryujinx.Graphics/Gpu/TextureReader.cs index b3b016ed9..4c3b4fb17 100644 --- a/Ryujinx.Graphics/Gpu/TextureReader.cs +++ b/Ryujinx.Graphics/Gpu/TextureReader.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.Gpu { - static class TextureReader + public static class TextureReader { public static byte[] Read(AMemory Memory, Texture Texture) { diff --git a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs index 2142e2c20..7d99279cd 100644 --- a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs +++ b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Gpu { - enum TextureSwizzle + public enum TextureSwizzle { _1dBuffer = 0, PitchColorKey = 1, diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index d6a33edbf..49338247e 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -38,8 +38,6 @@ namespace Ryujinx protected override void OnLoad(EventArgs e) { VSync = VSyncMode.On; - - Renderer.InitializeFrameBuffer(); } protected override void OnUpdateFrame(FrameEventArgs e)