diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 11236206bc..8a5ef4e9c2 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) @@ -296,8 +287,6 @@ namespace Ryujinx.Core.OsHle.Services.Android NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0); - Renderer.SetFrameBuffer(Map.GpuAddress); - long Address = Map.CpuAddress; if (MapFb.HasBufferOffset(Slot)) @@ -369,41 +358,24 @@ namespace Ryujinx.Core.OsHle.Services.Android Rotate = -MathF.PI * 0.5f; } - lock (RenderQueueLock) + if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(Map.GpuAddress)) { - if (NvFlingerDisposed) - { - return; - } + //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(Map.GpuAddress); + } + 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(Address, FbWidth, FbHeight); - Interlocked.Increment(ref RenderQueueCount); + 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) @@ -422,6 +394,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; @@ -437,7 +421,7 @@ namespace Ryujinx.Core.OsHle.Services.Android Logging.Debug("Waiting for a free BufferQueue slot..."); - if (!KeepRunning) + if (Disposed) { break; } @@ -447,7 +431,7 @@ namespace Ryujinx.Core.OsHle.Services.Android WaitBufferFree.WaitOne(); } - while (KeepRunning); + while (!Disposed); Logging.Debug($"Found free BufferQueue slot {Slot}!"); @@ -487,26 +471,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/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 6e72b7df41..36747609fe 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); @@ -43,10 +34,12 @@ namespace Ryujinx.Graphics.Gal void BindFrameBuffer(long Tag); - void BindFrameBufferTexture(long Tag, int Index); + void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler); void SetFrameBuffer(long Tag); + void SetFrameBuffer(byte[] Data, int Width, int Height); + //Rasterizer void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); @@ -72,10 +65,8 @@ namespace Ryujinx.Graphics.Gal void BindProgram(); //Texture - void SetTexture(int Index, GalTexture Tex); + void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler); void BindTexture(int Index); - - void SetSampler(GalTextureSampler Sampler); } } \ 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 7e7725d611..0000000000 --- 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/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs index 7a194b18ca..a338e75625 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -23,13 +23,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL private Dictionary Fbs; - private FrameBuffer CurrentFb; - private ShaderProgram Shader; private bool IsInitialized; - private int FbHandle; + private int CurrFbHandle; + private int CurrTexHandle; + private int RawFbTexHandle; private int VaoHandle; private int VboHandle; @@ -47,12 +47,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL return; } + Width = 1280; + Height = 720; + FrameBuffer Fb = new FrameBuffer(); - Fb.Handle = GL.GenFramebuffer(); + Fb.Handle = GL.GenFramebuffer(); Fb.RbHandle = GL.GenRenderbuffer(); Fb.TexHandle = GL.GenTexture(); + SetupTexture(Fb.TexHandle, Width, Height); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle); @@ -60,8 +65,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.RenderbufferStorage( RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, - 1280, - 720); + Width, + Height); GL.FramebufferRenderbuffer( FramebufferTarget.Framebuffer, @@ -69,14 +74,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL RenderbufferTarget.Renderbuffer, Fb.RbHandle); - GL.BindTexture(TextureTarget.Texture2D, Fb.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, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); - - GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fb.TexHandle, 0); + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0, + Fb.TexHandle, + 0); GL.DrawBuffer(DrawBufferMode.ColorAttachment0); @@ -89,7 +91,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); - FbHandle = Fb.Handle; + CurrFbHandle = Fb.Handle; } } @@ -107,24 +109,39 @@ namespace Ryujinx.Graphics.Gal.OpenGL { if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) { - CurrentFb = Fb; + CurrTexHandle = Fb.TexHandle; } } + public void Set(byte[] Data, int Width, int Height) + { + EnsureInitialized(); + + GL.ActiveTexture(TextureUnit.Texture0); + + 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 Render() { - if (CurrentFb.TexHandle != 0) + 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.ActiveTexture(TextureUnit.Texture0); - - GL.BindTexture(TextureTarget.Texture2D, CurrentFb.TexHandle); - GL.BindVertexArray(VaoHandle); GL.UseProgram(Shader.Handle); @@ -132,7 +149,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); //Restore the original state. - GL.BindFramebuffer(FramebufferTarget.Framebuffer, FbHandle); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, CurrFbHandle); GL.UseProgram(CurrentProgram); } @@ -146,6 +163,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL SetupShader(); SetupVertex(); + + RawFbTexHandle = GL.GenTexture(); + + SetupTexture(RawFbTexHandle, 1280, 720); } } @@ -217,5 +238,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 08ee840900..9ea25056cd 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -17,6 +17,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL Bind(Index); + const int Level = 0; //TODO: Support mipmap textures. const int Border = 0; if (IsCompressedTextureFormat(Texture.Format)) @@ -25,7 +26,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.CompressedTexImage2D( TextureTarget.Texture2D, - 0, + Level, InternalFmt, Texture.Width, Texture.Height, @@ -37,17 +38,17 @@ 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); } } @@ -59,7 +60,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindTexture(TextureTarget.Texture2D, Handle); } - public void Set(GalTextureSampler Sampler) + 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 3782127355..9923dc398b 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -1,4 +1,3 @@ -using OpenTK; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -19,8 +18,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL private ConcurrentQueue ActionsQueue; - private FrameBuffer FbRenderer; - public OpenGLRenderer() { Blend = new OGLBlend(); @@ -36,16 +33,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,34 +50,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) @@ -143,9 +108,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag)); } - public void BindFrameBufferTexture(long Tag, int Index) + public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler) { - ActionsQueue.Enqueue(() => FrameBuffer.BindTexture(Tag, Index)); + ActionsQueue.Enqueue(() => + { + FrameBuffer.BindTexture(Tag, Index); + + OGLTexture.Set(Sampler); + }); } public void SetFrameBuffer(long Tag) @@ -153,6 +123,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => FrameBuffer.Set(Tag)); } + public void SetFrameBuffer(byte[] Data, int Width, int Height) + { + ActionsQueue.Enqueue(() => FrameBuffer.Set(Data, Width, Height)); + } + public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) { ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags)); @@ -245,19 +220,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => Shader.BindProgram()); } - public void SetTexture(int Index, GalTexture Texture) + public void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler) { - ActionsQueue.Enqueue(() => this.Texture.Set(Index, Texture)); + ActionsQueue.Enqueue(() => + { + this.Texture.Set(Index, Texture); + + OGLTexture.Set(Sampler); + }); } public void BindTexture(int Index) { ActionsQueue.Enqueue(() => Texture.Bind(Index)); } - - public void SetSampler(GalTextureSampler Sampler) - { - ActionsQueue.Enqueue(() => Texture.Set(Sampler)); - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index e155e47539..82dbff9fac 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 6553cfcfd3..33f5823160 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 7bebea6250..e44d5b7d75 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 1a96d3be90..c920d9fa50 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 9ce7cbe312..0000000000 --- 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 5738050255..9a2e901288 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 33fc4a3b1b..4869886b9a 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; } @@ -249,6 +249,8 @@ namespace Ryujinx.Graphics.Gpu TicPosition += TicIndex * 0x20; TscPosition += TscIndex * 0x20; + GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition); + long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff; if (FrameBuffers.Contains(TextureAddress)) @@ -257,15 +259,15 @@ namespace Ryujinx.Graphics.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); + Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler); } else { - Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition)); + GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition); + + Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler); Gpu.Renderer.BindTexture(TexIndex); } - - Gpu.Renderer.SetSampler(TextureFactory.MakeSampler(Gpu, Memory, TscPosition)); } private void UploadUniforms(AMemory Memory) @@ -488,5 +490,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/Texture.cs b/Ryujinx.Graphics/Gpu/Texture.cs index 831c664d4f..cbfa683dc9 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 b3b016ed9a..4c3b4fb17e 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 2142e2c20a..7d99279cd4 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 d6a33edbfd..49338247e2 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)