From f39ced70c19af344fb9896f47df2f2b2b3dcff35 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 12 Apr 2018 02:09:20 -0300 Subject: [PATCH] Initial support for using frame buffer as texture --- .../OsHle/Services/Nv/INvDrvServices.cs | 2 + Ryujinx.Core/OsHle/Services/Nv/NvMap.cs | 1 + Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 2 + Ryujinx.Graphics/Gal/IGalRenderer.cs | 12 ++- Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs | 97 +++++++++++++------ Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs | 9 +- Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs | 33 ++++--- Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs | 63 ++++++++---- 8 files changed, 151 insertions(+), 68 deletions(-) diff --git a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs index e41f03a430..abda5b862f 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 9ef703196a..f3dd1f4718 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 45b99ead1b..11236206bc 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -296,6 +296,8 @@ 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)) diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 99534672d1..6e72b7df41 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -39,11 +39,13 @@ 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); + + void SetFrameBuffer(long Tag); //Rasterizer void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); @@ -72,6 +74,8 @@ namespace Ryujinx.Graphics.Gal //Texture void SetTexture(int Index, GalTexture Tex); - void SetSampler(int Index, GalTextureSampler Sampler); + void BindTexture(int Index); + + void SetSampler(GalTextureSampler Sampler); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs index f9c42ae014..7a194b18ca 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -1,6 +1,7 @@ using OpenTK; using OpenTK.Graphics.OpenGL; using System; +using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { @@ -8,7 +9,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { private struct FrameBuffer { - public int FbHandle; + public int Handle; public int RbHandle; public int TexHandle; } @@ -20,83 +21,121 @@ namespace Ryujinx.Graphics.Gal.OpenGL public int FpHandle; } - private FrameBuffer[] Fbs; + private Dictionary Fbs; + + private FrameBuffer CurrentFb; private ShaderProgram Shader; private bool IsInitialized; + private int FbHandle; 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.ContainsKey(Tag)) { return; } - Fbs[Index].FbHandle = GL.GenFramebuffer(); - Fbs[Index].RbHandle = GL.GenRenderbuffer(); - Fbs[Index].TexHandle = GL.GenTexture(); + FrameBuffer Fb = new FrameBuffer(); - GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle); + Fb.Handle = GL.GenFramebuffer(); + Fb.RbHandle = GL.GenRenderbuffer(); + Fb.TexHandle = GL.GenTexture(); - 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, + 1280, + 720); - GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle); + GL.FramebufferRenderbuffer( + FramebufferTarget.Framebuffer, + FramebufferAttachment.DepthStencilAttachment, + 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, 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); + FbHandle = 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); } + } - EnsureInitialized(); + public void Set(long Tag) + { + if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) + { + CurrentFb = Fb; + } + } - GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); + public void Render() + { + if (CurrentFb.TexHandle != 0) + { + EnsureInitialized(); - GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle); + int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); - GL.ActiveTexture(TextureUnit.Texture0); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); - GL.BindVertexArray(VaoHandle); + GL.ActiveTexture(TextureUnit.Texture0); - GL.UseProgram(Shader.Handle); + GL.BindTexture(TextureTarget.Texture2D, CurrentFb.TexHandle); - GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); + GL.BindVertexArray(VaoHandle); + + GL.UseProgram(Shader.Handle); + + GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); + + //Restore the original state. + GL.BindFramebuffer(FramebufferTarget.Framebuffer, FbHandle); + + GL.UseProgram(CurrentProgram); + } } private void EnsureInitialized() diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index b7c8999ee1..08ee840900 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -15,9 +15,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GL.ActiveTexture(TextureUnit.Texture0 + Index); - int Handle = EnsureTextureInitialized(Index); - - GL.BindTexture(TextureTarget.Texture2D, Handle); + Bind(Index); const int Border = 0; @@ -54,12 +52,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - public void Set(int Index, GalTextureSampler Sampler) + public void Bind(int Index) { int Handle = EnsureTextureInitialized(Index); GL.BindTexture(TextureTarget.Texture2D, Handle); + } + public 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 0b7bf92ac4..3782127355 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -63,7 +63,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Render() { - FbRenderer.Render(); + //FbRenderer.Render(); + FrameBuffer.Render(); } public void SetWindowSize(int Width, int Height) @@ -132,19 +133,24 @@ 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) { - ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex)); + ActionsQueue.Enqueue(() => FrameBuffer.BindTexture(Tag, Index)); + } + + public void SetFrameBuffer(long Tag) + { + ActionsQueue.Enqueue(() => FrameBuffer.Set(Tag)); } public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) @@ -239,14 +245,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue.Enqueue(() => Shader.BindProgram()); } - public void SetTexture(int Index, GalTexture Tex) + public void SetTexture(int Index, GalTexture Texture) { - ActionsQueue.Enqueue(() => Texture.Set(Index, Tex)); + ActionsQueue.Enqueue(() => this.Texture.Set(Index, Texture)); } - public void SetSampler(int Index, GalTextureSampler Sampler) + public void BindTexture(int Index) { - ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler)); + 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/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs index f4486f46cf..33fc4a3b1b 100644 --- a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs @@ -20,7 +20,9 @@ namespace Ryujinx.Graphics.Gpu public int Size; } - private ConstBuffer[] Cbs; + private ConstBuffer[] ConstBuffers; + + private HashSet FrameBuffers; private bool HasDataToRender; @@ -48,7 +50,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) @@ -85,8 +89,6 @@ namespace Ryujinx.Graphics.Gpu if (HasDataToRender) { HasDataToRender = false; - - Gpu.Renderer.DrawFrameBuffer(0); } int Arg0 = PBEntry.Arguments[0]; @@ -99,16 +101,20 @@ namespace Ryujinx.Graphics.Gpu SetFrameBuffer(0); - Gpu.Renderer.ClearBuffers(Layer, Flags); + //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); + + Gpu.Renderer.CreateFrameBuffer(Address, Width, Height); + Gpu.Renderer.BindFrameBuffer(Address); } private long[] UploadShaders(AMemory Memory) @@ -205,11 +211,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 +249,23 @@ 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)); + 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); + } + else + { + Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition)); + Gpu.Renderer.BindTexture(TexIndex); + } + + Gpu.Renderer.SetSampler(TextureFactory.MakeSampler(Gpu, Memory, TscPosition)); } private void UploadUniforms(AMemory Memory) @@ -262,9 +285,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) { @@ -414,16 +437,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);