From b723bd5e4a452abc1d417ed3d8381ac742f6c8d9 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Sun, 5 Aug 2018 20:30:25 -0300 Subject: [PATCH] Use textures for framebuffers and split color and zeta framebuffers --- Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs | 2 +- Ryujinx.Graphics/Gal/IGalFrameBuffer.cs | 13 +- Ryujinx.Graphics/Gal/IGalRasterizer.cs | 1 + Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs | 48 ++ .../Gal/OpenGL/OGLEnumConverter.cs | 69 +++ Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs | 579 ++++++++++++------ Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs | 15 +- Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs | 4 - Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs | 43 +- Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs | 1 + 10 files changed, 557 insertions(+), 218 deletions(-) create mode 100644 Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs diff --git a/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs b/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs index 90e6b1e7e4..670c51b265 100644 --- a/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs +++ b/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs @@ -19,7 +19,7 @@ RG32Sint = 0xcc, RG32Uint = 0xcd, RGBX16Float = 0xce, - BGRA8Unorm = 0x0cf, + BGRA8Unorm = 0xcf, BGRA8Srgb = 0xd0, RGB10A2Unorm = 0xd1, RGB10A2Uint = 0xd2, diff --git a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs index c0287ef8be..91608ffa77 100644 --- a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs @@ -4,9 +4,17 @@ namespace Ryujinx.Graphics.Gal { public interface IGalFrameBuffer { - void Create(long Key, int Width, int Height); + void CreateColor(long Key, int Width, int Height, GalFrameBufferFormat Format); - void Bind(long Key); + void BindColor(long Key, int Attachment); + + void UnbindColor(int Attachment); + + void CreateZeta(long Key, int Width, int Height, GalZetaFormat Format); + + void BindZeta(long Key); + + void UnbindZeta(); void BindTexture(long Key, int Index); @@ -40,7 +48,6 @@ namespace Ryujinx.Graphics.Gal long Key, int Width, int Height, - GalTextureFormat Format, byte[] Buffer); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index 89e50b1f1b..a20b6f5322 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Gal void ClearBuffers( GalClearBufferFlags Flags, + int Attachment, float Red, float Green, float Blue, float Alpha, float Depth, int Stencil); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs new file mode 100644 index 0000000000..6e41c85a28 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs @@ -0,0 +1,48 @@ +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + public class OGLBlend : IGalBlend + { + public void Enable() + { + GL.Enable(EnableCap.Blend); + } + + public void Disable() + { + GL.Disable(EnableCap.Blend); + } + + public void Set( + GalBlendEquation Equation, + GalBlendFactor FuncSrc, + GalBlendFactor FuncDst) + { + GL.BlendEquation(OGLEnumConverter.GetBlendEquation(Equation)); + + GL.BlendFunc( + OGLEnumConverter.GetBlendFactor(FuncSrc), + OGLEnumConverter.GetBlendFactor(FuncDst)); + } + + public void SetSeparate( + GalBlendEquation EquationRgb, + GalBlendEquation EquationAlpha, + GalBlendFactor FuncSrcRgb, + GalBlendFactor FuncDstRgb, + GalBlendFactor FuncSrcAlpha, + GalBlendFactor FuncDstAlpha) + { + GL.BlendEquationSeparate( + OGLEnumConverter.GetBlendEquation(EquationRgb), + OGLEnumConverter.GetBlendEquation(EquationAlpha)); + + GL.BlendFuncSeparate( + (BlendingFactorSrc)OGLEnumConverter.GetBlendFactor(FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(FuncDstRgb), + (BlendingFactorSrc)OGLEnumConverter.GetBlendFactor(FuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(FuncDstAlpha)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 3c42e5d387..28665026e5 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -125,6 +125,75 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentException(nameof(Type)); } + public static PixelInternalFormat GetFrameBufferInternalFormat(GalFrameBufferFormat Format) + { + switch (Format) + { + //Sometimes it's not set, use a safe format + case 0: return PixelInternalFormat.Rgba8; + + case GalFrameBufferFormat.RGBA32Float: return PixelInternalFormat.Rgba32f; + case GalFrameBufferFormat.RGBA32Sint: return PixelInternalFormat.Rgba32i; + case GalFrameBufferFormat.RGBA32Uint: return PixelInternalFormat.Rgba32ui; + case GalFrameBufferFormat.RGBA16Unorm: return PixelInternalFormat.Rgba16; + case GalFrameBufferFormat.RGBA16Snorm: return PixelInternalFormat.Rgba16Snorm; + case GalFrameBufferFormat.RGBA16Sint: return PixelInternalFormat.Rgba16i; + case GalFrameBufferFormat.RGBA16Uint: return PixelInternalFormat.Rgba16ui; + case GalFrameBufferFormat.RGBA16Float: return PixelInternalFormat.Rgba16f; + case GalFrameBufferFormat.RG32Float: return PixelInternalFormat.Rg32f; + case GalFrameBufferFormat.RG32Sint: return PixelInternalFormat.Rg32i; + case GalFrameBufferFormat.RG32Uint: return PixelInternalFormat.Rg32ui; + case GalFrameBufferFormat.RGB10A2Unorm: return PixelInternalFormat.Rgb10A2; + case GalFrameBufferFormat.RGB10A2Uint: return PixelInternalFormat.Rgb10A2ui; + case GalFrameBufferFormat.RGBA8Unorm: return PixelInternalFormat.Rgba8; + case GalFrameBufferFormat.RGBA8Srgb: return PixelInternalFormat.Srgb8; + case GalFrameBufferFormat.RG16Snorm: return PixelInternalFormat.Rg16Snorm; + case GalFrameBufferFormat.R11G11B10Float: return PixelInternalFormat.R11fG11fB10f; + case GalFrameBufferFormat.R32Float: return PixelInternalFormat.R32f; + case GalFrameBufferFormat.R16Float: return PixelInternalFormat.R16f; + case GalFrameBufferFormat.R8Unorm: return PixelInternalFormat.R8; + case GalFrameBufferFormat.R8Snorm: return PixelInternalFormat.R8Snorm; + case GalFrameBufferFormat.R8Sint: return PixelInternalFormat.R8i; + case GalFrameBufferFormat.R8Uint: return PixelInternalFormat.R8ui; + } + + throw new NotImplementedException(Format.ToString()); + } + + public static (PixelFormat Format, PixelType Type) GetFrameBufferFormat(GalFrameBufferFormat Format) + { + switch (Format) + { + case 0: return (PixelFormat.Rgba, PixelType.UnsignedByte); + + case GalFrameBufferFormat.RGBA32Float: return (PixelFormat.Rgba, PixelType.Float); + case GalFrameBufferFormat.RGBA32Sint: return (PixelFormat.Rgba, PixelType.Int); + case GalFrameBufferFormat.RGBA32Uint: return (PixelFormat.Rgba, PixelType.UnsignedInt); + case GalFrameBufferFormat.RGBA16Unorm: return (PixelFormat.Rgba, PixelType.UnsignedShort); + case GalFrameBufferFormat.RGBA16Snorm: return (PixelFormat.Rgba, PixelType.Short); + case GalFrameBufferFormat.RGBA16Sint: return (PixelFormat.Rgba, PixelType.Short); + case GalFrameBufferFormat.RGBA16Uint: return (PixelFormat.Rgba, PixelType.UnsignedShort); + case GalFrameBufferFormat.RGBA16Float: return (PixelFormat.Rgba, PixelType.HalfFloat); + case GalFrameBufferFormat.RG32Float: return (PixelFormat.Rg, PixelType.Float); + case GalFrameBufferFormat.RG32Sint: return (PixelFormat.Rg, PixelType.Int); + case GalFrameBufferFormat.RG32Uint: return (PixelFormat.Rg, PixelType.UnsignedInt); + case GalFrameBufferFormat.RGB10A2Unorm: return (PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); + case GalFrameBufferFormat.RGB10A2Uint: return (PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); + case GalFrameBufferFormat.RGBA8Unorm: return (PixelFormat.Rgba, PixelType.UnsignedByte); + case GalFrameBufferFormat.RGBA8Srgb: return (PixelFormat.Rgba, PixelType.UnsignedByte); + case GalFrameBufferFormat.RG16Snorm: return (PixelFormat.Rg, PixelType.Short); + case GalFrameBufferFormat.R11G11B10Float: return (PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); + case GalFrameBufferFormat.R32Float: return (PixelFormat.Red, PixelType.Float); + case GalFrameBufferFormat.R16Float: return (PixelFormat.Red, PixelType.HalfFloat); + case GalFrameBufferFormat.R8Unorm: return (PixelFormat.Red, PixelType.UnsignedByte); + case GalFrameBufferFormat.R8Snorm: return (PixelFormat.Red, PixelType.Byte); + case GalFrameBufferFormat.R8Sint: return (PixelFormat.Red, PixelType.Byte); + case GalFrameBufferFormat.R8Uint: return (PixelFormat.Red, PixelType.UnsignedByte); + } + + throw new NotImplementedException(Format.ToString()); + } + public static (PixelFormat, PixelType) GetTextureFormat(GalTextureFormat Format) { switch (Format) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs index 30a3de64a0..9da9853dae 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -16,50 +16,163 @@ namespace Ryujinx.Graphics.Gal.OpenGL public Rect(int X, int Y, int Width, int Height) { - this.X = X; - this.Y = Y; - this.Width = Width; + this.X = X; + this.Y = Y; + this.Width = Width; this.Height = Height; } } - private class FrameBuffer + private class Texture { - public int Width { get; set; } - public int Height { get; set; } + public int Width { get; private set; } + public int Height { get; private set; } - public int Handle { get; private set; } - public int RbHandle { get; private set; } - public int TexHandle { get; private set; } + public PixelInternalFormat InternalFormat { get; private set; } + public PixelFormat Format { get; private set; } + public PixelType Type { get; private set; } - public FrameBuffer(int Width, int Height, bool HasRenderBuffer) + public int Handle { get; private set; } + + private bool Initialized; + + public Texture() { - this.Width = Width; - this.Height = Height; + Handle = GL.GenTexture(); + } - Handle = GL.GenFramebuffer(); - TexHandle = GL.GenTexture(); - - if (HasRenderBuffer) + public void EnsureSetup( + int Width, + int Height, + PixelInternalFormat InternalFormat, + PixelFormat Format, + PixelType Type) + { + if (!Initialized || + this.Width != Width || + this.Height != Height || + this.InternalFormat != InternalFormat) { - RbHandle = GL.GenRenderbuffer(); + int CopyBuffer = 0; + + bool ChangingFormat = Initialized && this.InternalFormat != InternalFormat; + + GL.BindTexture(TextureTarget.Texture2D, Handle); + + if (ChangingFormat) + { + CopyBuffer = GL.GenBuffer(); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer); + + int MaxWidth = Math.Max(Width, this.Width); + int MaxHeight = Math.Max(Height, this.Height); + + //TODO: Dehardcode size number + GL.BufferData(BufferTarget.PixelPackBuffer, MaxWidth * MaxHeight * MaxBpp, IntPtr.Zero, BufferUsageHint.StaticCopy); + + GL.GetTexImage(TextureTarget.Texture2D, 0, this.Format, this.Type, IntPtr.Zero); + + GL.DeleteTexture(Handle); + + Handle = GL.GenTexture(); + + GL.BindTexture(TextureTarget.Texture2D, Handle); + } + + const int MinFilter = (int)TextureMinFilter.Linear; + const int MagFilter = (int)TextureMagFilter.Linear; + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter); + + const int Level = 0; + const int Border = 0; + + GL.TexImage2D( + TextureTarget.Texture2D, + Level, + InternalFormat, + Width, + Height, + Border, + Format, + Type, + IntPtr.Zero); + + if (ChangingFormat) + { + GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); + + GL.DeleteBuffer(CopyBuffer); + } + + this.Width = Width; + this.Height = Height; + this.InternalFormat = InternalFormat; + this.Format = Format; + this.Type = Type; + + Initialized = true; } } + + public void EnsureSetup(int Width, int Height, GalFrameBufferFormat Format) + { + //TODO: Convert color format + + EnsureSetup( + Width, + Height, + PixelInternalFormat.Rgba8, + PixelFormat.Rgba, + PixelType.UnsignedByte); + } + + public void EnsureSetup(int Width, int Height, GalZetaFormat Format) + { + //TODO: Convert zeta format + + EnsureSetup( + Width, + Height, + PixelInternalFormat.Depth24Stencil8, + PixelFormat.DepthStencil, + PixelType.UnsignedInt248); + } } + private static readonly DrawBuffersEnum[] DrawBuffers = new DrawBuffersEnum[] + { + DrawBuffersEnum.ColorAttachment0, + DrawBuffersEnum.ColorAttachment1, + DrawBuffersEnum.ColorAttachment2, + DrawBuffersEnum.ColorAttachment3, + DrawBuffersEnum.ColorAttachment4, + DrawBuffersEnum.ColorAttachment5, + DrawBuffersEnum.ColorAttachment6, + DrawBuffersEnum.ColorAttachment7, + }; + private const int NativeWidth = 1280; private const int NativeHeight = 720; - private Dictionary Fbs; + //TODO: Use a variable value here + private const int MaxBpp = 16; + + private const GalTextureFormat RawFormat = GalTextureFormat.A8B8G8R8; + + private Dictionary ColorTextures; + private Dictionary ZetaTextures; + + private Texture RawTex; + private Texture ReadTex; private Rect Viewport; private Rect Window; - private FrameBuffer CurrFb; - private FrameBuffer CurrReadFb; - - private FrameBuffer RawFb; - private bool FlipX; private bool FlipY; @@ -68,111 +181,137 @@ namespace Ryujinx.Graphics.Gal.OpenGL private int CropRight; private int CropBottom; + private int DummyFrameBuffer; + + private int SrcFb; + private int DstFb; + public OGLFrameBuffer() { - Fbs = new Dictionary(); + ColorTextures = new Dictionary(); + + ZetaTextures = new Dictionary(); } - public void Create(long Key, int Width, int Height) + public void CreateColor(long Key, int Width, int Height, GalFrameBufferFormat Format) { - if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) + if (!ColorTextures.TryGetValue(Key, out Texture Tex)) { - if (Fb.Width != Width || - Fb.Height != Height) - { - SetupTexture(Fb.TexHandle, Width, Height); + Tex = new Texture(); - Fb.Width = Width; - Fb.Height = Height; - } - - return; + ColorTextures.Add(Key, Tex); } - Fb = new FrameBuffer(Width, Height, true); + Tex.EnsureSetup(Width, Height, Format); + } - SetupTexture(Fb.TexHandle, Width, Height); + public void BindColor(long Key, int Attachment) + { + if (ColorTextures.TryGetValue(Key, out Texture Tex)) + { + EnsureFrameBuffer(); - GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0 + Attachment, + Tex.Handle, + 0); + } + else + { + UnbindColor(Attachment); + } + } - GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle); - - GL.RenderbufferStorage( - RenderbufferTarget.Renderbuffer, - RenderbufferStorage.Depth24Stencil8, - Width, - Height); - - GL.FramebufferRenderbuffer( - FramebufferTarget.Framebuffer, - FramebufferAttachment.DepthStencilAttachment, - RenderbufferTarget.Renderbuffer, - Fb.RbHandle); + public void UnbindColor(int Attachment) + { + EnsureFrameBuffer(); GL.FramebufferTexture( FramebufferTarget.Framebuffer, - FramebufferAttachment.ColorAttachment0, - Fb.TexHandle, + FramebufferAttachment.ColorAttachment0 + Attachment, + 0, 0); - - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); - - Fbs.Add(Key, Fb); } - public void Bind(long Key) + public void CreateZeta(long Key, int Width, int Height, GalZetaFormat Format) { - if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) + if (!ZetaTextures.TryGetValue(Key, out Texture Tex)) { - GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); + Tex = new Texture(); - CurrFb = Fb; + ZetaTextures.Add(Key, Tex); } + + Tex.EnsureSetup(Width, Height, Format); + } + + public void BindZeta(long Key) + { + if (ZetaTextures.TryGetValue(Key, out Texture Tex)) + { + EnsureFrameBuffer(); + + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.DepthStencilAttachment, + Tex.Handle, + 0); + } + else + { + UnbindZeta(); + } + } + + public void UnbindZeta() + { + EnsureFrameBuffer(); + + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.DepthStencilAttachment, + 0, + 0); } public void BindTexture(long Key, int Index) { - if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) + Texture Tex; + + if (ColorTextures.TryGetValue(Key, out Tex) || + ZetaTextures.TryGetValue(Key, out Tex)) { GL.ActiveTexture(TextureUnit.Texture0 + Index); - GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle); + GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); } } public void Set(long Key) { - if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) + if (ColorTextures.TryGetValue(Key, out Texture Tex)) { - CurrReadFb = Fb; + ReadTex = Tex; } } public void Set(byte[] Data, int Width, int Height) { - if (RawFb == null) + if (RawTex == null) { - CreateRawFb(Width, Height); + RawTex = new Texture(); } - if (RawFb.Width != Width || - RawFb.Height != Height) - { - SetupTexture(RawFb.TexHandle, Width, Height); + RawTex.EnsureSetup(Width, Height, PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte); - RawFb.Width = Width; - RawFb.Height = Height; - } + GL.BindTexture(TextureTarget.Texture2D, RawTex.Handle); - GL.ActiveTexture(TextureUnit.Texture0); - - GL.BindTexture(TextureTarget.Texture2D, RawFb.TexHandle); - - (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); + (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(RawFormat); GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data); - CurrReadFb = RawFb; + ReadTex = RawTex; } public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom) @@ -209,60 +348,71 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Render() { - if (CurrReadFb != null) + if (ReadTex == null) { - int SrcX0, SrcX1, SrcY0, SrcY1; - - if (CropLeft == 0 && CropRight == 0) - { - SrcX0 = 0; - SrcX1 = CurrReadFb.Width; - } - else - { - SrcX0 = CropLeft; - SrcX1 = CropRight; - } - - if (CropTop == 0 && CropBottom == 0) - { - SrcY0 = 0; - SrcY1 = CurrReadFb.Height; - } - else - { - SrcY0 = CropTop; - SrcY1 = CropBottom; - } - - float RatioX = MathF.Min(1f, (Window.Height * (float)NativeWidth) / ((float)NativeHeight * Window.Width)); - float RatioY = MathF.Min(1f, (Window.Width * (float)NativeHeight) / ((float)NativeWidth * Window.Height)); - - int DstWidth = (int)(Window.Width * RatioX); - int DstHeight = (int)(Window.Height * RatioY); - - int DstPaddingX = (Window.Width - DstWidth) / 2; - int DstPaddingY = (Window.Height - DstHeight) / 2; - - int DstX0 = FlipX ? Window.Width - DstPaddingX : DstPaddingX; - int DstX1 = FlipX ? DstPaddingX : Window.Width - DstPaddingX; - - int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY; - int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY; - - GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); - - GL.Viewport(0, 0, Window.Width, Window.Height); - - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, CurrReadFb.Handle); - - GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); - - GL.BlitFramebuffer( - SrcX0, SrcY0, SrcX1, SrcY1, - DstX0, DstY0, DstX1, DstY1, - ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); + return; } + + int SrcX0, SrcX1, SrcY0, SrcY1; + + if (CropLeft == 0 && CropRight == 0) + { + SrcX0 = 0; + SrcX1 = ReadTex.Width; + } + else + { + SrcX0 = CropLeft; + SrcX1 = CropRight; + } + + if (CropTop == 0 && CropBottom == 0) + { + SrcY0 = 0; + SrcY1 = ReadTex.Height; + } + else + { + SrcY0 = CropTop; + SrcY1 = CropBottom; + } + + float RatioX = MathF.Min(1f, (Window.Height * (float)NativeWidth) / ((float)NativeHeight * Window.Width)); + float RatioY = MathF.Min(1f, (Window.Width * (float)NativeHeight) / ((float)NativeWidth * Window.Height)); + + int DstWidth = (int)(Window.Width * RatioX); + int DstHeight = (int)(Window.Height * RatioY); + + int DstPaddingX = (Window.Width - DstWidth) / 2; + int DstPaddingY = (Window.Height - DstHeight) / 2; + + int DstX0 = FlipX ? Window.Width - DstPaddingX : DstPaddingX; + int DstX1 = FlipX ? DstPaddingX : Window.Width - DstPaddingX; + + int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY; + int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY; + + if (SrcFb == 0) SrcFb = GL.GenFramebuffer(); + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); + + GL.Viewport(0, 0, Window.Width, Window.Height); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb); + + GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0); + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + + GL.Clear(ClearBufferMask.ColorBufferBit); + + GL.BlitFramebuffer( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); + + EnsureFrameBuffer(); } public void Copy( @@ -277,39 +427,61 @@ namespace Ryujinx.Graphics.Gal.OpenGL int DstX1, int DstY1) { - if (Fbs.TryGetValue(SrcKey, out FrameBuffer SrcFb) && - Fbs.TryGetValue(DstKey, out FrameBuffer DstFb)) + bool Found = false; + + if (ColorTextures.TryGetValue(SrcKey, out Texture SrcTex) && + ColorTextures.TryGetValue(DstKey, out Texture DstTex)) { - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb.Handle); - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb.Handle); - - GL.Clear(ClearBufferMask.ColorBufferBit); - - GL.BlitFramebuffer( + CopyTextures( SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, + SrcTex.Handle, + DstTex.Handle, + FramebufferAttachment.ColorAttachment0, ClearBufferMask.ColorBufferBit, - BlitFramebufferFilter.Linear); + true); + + Found = true; } -} + + if (ZetaTextures.TryGetValue(SrcKey, out Texture ZetaSrcTex) && + ZetaTextures.TryGetValue(DstKey, out Texture ZetaDstTex)) + { + CopyTextures( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + ZetaSrcTex.Handle, + ZetaDstTex.Handle, + FramebufferAttachment.DepthStencilAttachment, + ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit, + false); + + Found = true; + } + + if (Found) + { + EnsureFrameBuffer(); + } + } public void GetBufferData(long Key, Action Callback) { - if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) + Texture Tex; + + if (ColorTextures.TryGetValue(Key, out Tex) || + ZetaTextures.TryGetValue(Key, out Tex)) { - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle); + //Note: Change this value when framebuffer sizes are dehardcoded + byte[] Data = new byte[Tex.Width * Tex.Height * MaxBpp]; - byte[] Data = new byte[Fb.Width * Fb.Height * 4]; + GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); - (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); - - GL.ReadPixels( + GL.GetTexImage( + TextureTarget.Texture2D, 0, - 0, - Fb.Width, - Fb.Height, - Format, - Type, + Tex.Format, + Tex.Type, Data); Callback(Data); @@ -320,83 +492,88 @@ namespace Ryujinx.Graphics.Gal.OpenGL long Key, int Width, int Height, - GalTextureFormat Format, byte[] Buffer) { - if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) + Texture Tex; + + if (ColorTextures.TryGetValue(Key, out Tex) || + ZetaTextures.TryGetValue(Key, out Tex)) { - GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle); + GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); const int Level = 0; const int Border = 0; - const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba; - - (PixelFormat GlFormat, PixelType Type) = OGLEnumConverter.GetTextureFormat(Format); - GL.TexImage2D( TextureTarget.Texture2D, Level, - InternalFmt, + Tex.InternalFormat, Width, Height, Border, - GlFormat, - Type, + Tex.Format, + Tex.Type, Buffer); } } - private void CreateRawFb(int Width, int Height) + private void EnsureFrameBuffer() { - if (RawFb == null) + if (DummyFrameBuffer == 0) { - RawFb = new FrameBuffer(Width, Height, false); - - SetupTexture(RawFb.TexHandle, Width, Height); - - RawFb.Width = Width; - RawFb.Height = Height; - - GL.BindFramebuffer(FramebufferTarget.Framebuffer, RawFb.Handle); - - GL.FramebufferTexture( - FramebufferTarget.Framebuffer, - FramebufferAttachment.ColorAttachment0, - RawFb.TexHandle, - 0); - - GL.Viewport(0, 0, Width, Height); + DummyFrameBuffer = GL.GenFramebuffer(); } + + GL.BindFramebuffer(FramebufferTarget.Framebuffer, DummyFrameBuffer); + + GL.DrawBuffers(8, DrawBuffers); } - private void SetupTexture(int Handle, int Width, int Height) + private void CopyTextures( + int SrcX0, + int SrcY0, + int SrcX1, + int SrcY1, + int DstX0, + int DstY0, + int DstX1, + int DstY1, + int SrcTexture, + int DstTexture, + FramebufferAttachment Attachment, + ClearBufferMask Mask, + bool Color) { - GL.BindTexture(TextureTarget.Texture2D, Handle); + if (SrcFb == 0) SrcFb = GL.GenFramebuffer(); + if (DstFb == 0) DstFb = GL.GenFramebuffer(); - const int MinFilter = (int)TextureMinFilter.Linear; - const int MagFilter = (int)TextureMagFilter.Linear; + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter); + GL.FramebufferTexture( + FramebufferTarget.ReadFramebuffer, + Attachment, + SrcTexture, + 0); - (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + Attachment, + DstTexture, + 0); - const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba; + if (Color) + { + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + } - const int Level = 0; - const int Border = 0; + GL.Clear(Mask); - GL.TexImage2D( - TextureTarget.Texture2D, - Level, - InternalFmt, - Width, - Height, - Border, - Format, - Type, - IntPtr.Zero); + GL.BlitFramebuffer( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + Mask, + Color ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 0802147857..c0e4bd0f53 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -45,17 +45,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void ClearBuffers( GalClearBufferFlags Flags, + int Attachment, float Red, float Green, float Blue, float Alpha, float Depth, int Stencil) { - ClearBufferMask Mask = ClearBufferMask.ColorBufferBit; + //TODO: Handle attachment - GL.ColorMask( - Flags.HasFlag(GalClearBufferFlags.ColorRed), - Flags.HasFlag(GalClearBufferFlags.ColorGreen), - Flags.HasFlag(GalClearBufferFlags.ColorBlue), - Flags.HasFlag(GalClearBufferFlags.ColorAlpha)); + ClearBufferMask Mask = ClearBufferMask.ColorBufferBit; if (Flags.HasFlag(GalClearBufferFlags.Depth)) { @@ -67,6 +64,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL Mask |= ClearBufferMask.StencilBufferBit; } + GL.ColorMask( + Flags.HasFlag(GalClearBufferFlags.ColorRed), + Flags.HasFlag(GalClearBufferFlags.ColorGreen), + Flags.HasFlag(GalClearBufferFlags.ColorBlue), + Flags.HasFlag(GalClearBufferFlags.ColorAlpha)); + GL.ClearColor(Red, Green, Blue, Alpha); GL.ClearDepth(Depth); diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs index d2c5f1262b..7fb5ea8af4 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs @@ -154,16 +154,12 @@ namespace Ryujinx.HLE.Gpu.Engines } else if (IsDstFb) { - //Texture -> Frame Buffer copy. - const GalTextureFormat Format = GalTextureFormat.A8B8G8R8; - byte[] Buffer = TextureReader.Read(Vmm, SrcTexture()); Gpu.Renderer.FrameBuffer.SetBufferData( DstKey, DstWidth, DstHeight, - Format, Buffer); } else diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index 6f60124458..19488e7e0a 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -104,6 +104,8 @@ namespace Ryujinx.HLE.Gpu.Engines SetFrameBuffer(Vmm, 0); + SetZeta(Vmm); + long[] Keys = UploadShaders(Vmm); Gpu.Renderer.Shader.BindProgram(); @@ -152,6 +154,7 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Rasterizer.ClearBuffers( Flags, + FbIndex, Red, Green, Blue, Alpha, Depth, Stencil); @@ -161,13 +164,22 @@ namespace Ryujinx.HLE.Gpu.Engines { long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); + int Format = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10); + + if (VA == 0 || Format == 0) + { + Gpu.Renderer.FrameBuffer.UnbindColor(FbIndex); + + return; + } + long Key = Vmm.GetPhysicalAddress(VA); FrameBuffers.Add(Key); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); - + float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 4); float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 4); @@ -180,12 +192,37 @@ namespace Ryujinx.HLE.Gpu.Engines int VpW = (int)(TX + MathF.Abs(SX)) - VpX; int VpH = (int)(TY + MathF.Abs(SY)) - VpY; - Gpu.Renderer.FrameBuffer.Create(Key, Width, Height); - Gpu.Renderer.FrameBuffer.Bind(Key); + Gpu.Renderer.FrameBuffer.CreateColor(Key, Width, Height, (GalFrameBufferFormat)Format); + Gpu.Renderer.FrameBuffer.BindColor(Key, FbIndex); Gpu.Renderer.FrameBuffer.SetViewport(VpX, VpY, VpW, VpH); } + private void SetZeta(NvGpuVmm Vmm) + { + long ZA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); + + int Format = ReadRegister(NvGpuEngine3dReg.ZetaFormat); + + bool ZetaEnable = (ReadRegister(NvGpuEngine3dReg.ZetaEnable) & 1) != 0; + + if (ZA == 0 || Format == 0 || !ZetaEnable) + { + Gpu.Renderer.FrameBuffer.UnbindZeta(); + + return; + } + + long Key = Vmm.GetPhysicalAddress(ZA); + + int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz); + int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert); + + Gpu.Renderer.FrameBuffer.CreateZeta(Key, Width, Height, (GalZetaFormat)Format); + + Gpu.Renderer.FrameBuffer.BindZeta(Key); + } + private long[] UploadShaders(NvGpuVmm Vmm) { long[] Keys = new long[5]; diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs index b14a96deb3..b03aef0241 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs @@ -51,6 +51,7 @@ namespace Ryujinx.HLE.Gpu.Engines StencilFrontFuncMask = 0x4e6, StencilFrontMask = 0x4e7, VertexArrayElemBase = 0x50d, + ZetaEnable = 0x54e, TexHeaderPoolOffset = 0x55d, TexSamplerPoolOffset = 0x557, StencilTwoSideEnable = 0x565,