diff options
Diffstat (limited to 'Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs')
| -rw-r--r-- | Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs new file mode 100644 index 00000000..99bfa350 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs @@ -0,0 +1,497 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Texture; +using System; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OGLRenderTarget : IGalRenderTarget + { + private struct Rect + { + public int X { get; private set; } + public int Y { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } + + public Rect(int X, int Y, int Width, int Height) + { + this.X = X; + this.Y = Y; + this.Width = Width; + this.Height = Height; + } + } + + private const int NativeWidth = 1280; + private const int NativeHeight = 720; + + private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; + + private OGLTexture Texture; + + private ImageHandler RawTex; + private ImageHandler ReadTex; + + private Rect Viewport; + private Rect Window; + + private bool FlipX; + private bool FlipY; + + private int CropTop; + private int CropLeft; + private int CropRight; + private int CropBottom; + + //This framebuffer is used to attach guest rendertargets, + //think of it as a dummy OpenGL VAO + private int DummyFrameBuffer; + + //These framebuffers are used to blit images + private int SrcFb; + private int DstFb; + + //Holds current attachments, used to avoid unnecesary calls to OpenGL + private int[] ColorAttachments; + + private int DepthAttachment; + private int StencilAttachment; + + public OGLRenderTarget(OGLTexture Texture) + { + ColorAttachments = new int[8]; + + this.Texture = Texture; + } + + public void BindColor(long Key, int Attachment) + { + if (Texture.TryGetImage(Key, out ImageHandler Tex)) + { + EnsureFrameBuffer(); + + Attach(ref ColorAttachments[Attachment], Tex.Handle, FramebufferAttachment.ColorAttachment0 + Attachment); + } + else + { + UnbindColor(Attachment); + } + } + + public void UnbindColor(int Attachment) + { + EnsureFrameBuffer(); + + Attach(ref ColorAttachments[Attachment], 0, FramebufferAttachment.ColorAttachment0 + Attachment); + } + + public void BindZeta(long Key) + { + if (Texture.TryGetImage(Key, out ImageHandler Tex)) + { + EnsureFrameBuffer(); + + if (Tex.HasDepth && Tex.HasStencil) + { + if (DepthAttachment != Tex.Handle || + StencilAttachment != Tex.Handle) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthStencilAttachment, + Tex.Handle, + 0); + + DepthAttachment = Tex.Handle; + + StencilAttachment = Tex.Handle; + } + } + else if (Tex.HasDepth) + { + Attach(ref DepthAttachment, Tex.Handle, FramebufferAttachment.DepthAttachment); + + Attach(ref StencilAttachment, 0, FramebufferAttachment.StencilAttachment); + } + else if (Tex.HasStencil) + { + Attach(ref DepthAttachment, 0, FramebufferAttachment.DepthAttachment); + + Attach(ref StencilAttachment, Tex.Handle, FramebufferAttachment.StencilAttachment); + } + else + { + throw new InvalidOperationException(); + } + } + else + { + UnbindZeta(); + } + } + + public void UnbindZeta() + { + EnsureFrameBuffer(); + + if (DepthAttachment != 0 || + StencilAttachment != 0) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthStencilAttachment, + 0, + 0); + + DepthAttachment = 0; + + StencilAttachment = 0; + } + } + + public void BindTexture(long Key, int Index) + { + if (Texture.TryGetImage(Key, out ImageHandler Tex)) + { + GL.ActiveTexture(TextureUnit.Texture0 + Index); + + GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); + } + } + + public void Set(long Key) + { + if (Texture.TryGetImage(Key, out ImageHandler Tex)) + { + ReadTex = Tex; + } + } + + public void Set(byte[] Data, int Width, int Height) + { + if (RawTex == null) + { + RawTex = new ImageHandler(); + } + + RawTex.EnsureSetup(new GalImage(Width, Height, RawFormat)); + + GL.BindTexture(TextureTarget.Texture2D, RawTex.Handle); + + GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, RawTex.PixelFormat, RawTex.PixelType, Data); + + ReadTex = RawTex; + } + + public void SetMap(int[] Map) + { + if (Map != null && Map.Length > 0) + { + DrawBuffersEnum[] Mode = new DrawBuffersEnum[Map.Length]; + + for (int i = 0; i < Map.Length; i++) + { + Mode[i] = DrawBuffersEnum.ColorAttachment0 + Map[i]; + } + + GL.DrawBuffers(Mode.Length, Mode); + } + else + { + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + } + } + + public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom) + { + this.FlipX = FlipX; + this.FlipY = FlipY; + + CropTop = Top; + CropLeft = Left; + CropRight = Right; + CropBottom = Bottom; + } + + public void SetWindowSize(int Width, int Height) + { + Window = new Rect(0, 0, Width, Height); + } + + public void SetViewport(int X, int Y, int Width, int Height) + { + Viewport = new Rect(X, Y, Width, Height); + + SetViewport(Viewport); + } + + private void SetViewport(Rect Viewport) + { + GL.Viewport( + Viewport.X, + Viewport.Y, + Viewport.Width, + Viewport.Height); + } + + public void Render() + { + if (ReadTex == null) + { + 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( + long SrcKey, + long DstKey, + int SrcX0, + int SrcY0, + int SrcX1, + int SrcY1, + int DstX0, + int DstY0, + int DstX1, + int DstY1) + { + if (Texture.TryGetImage(SrcKey, out ImageHandler SrcTex) && + Texture.TryGetImage(DstKey, out ImageHandler DstTex)) + { + if (SrcTex.HasColor != DstTex.HasColor || + SrcTex.HasDepth != DstTex.HasDepth || + SrcTex.HasStencil != DstTex.HasStencil) + { + throw new NotImplementedException(); + } + + if (SrcTex.HasColor) + { + CopyTextures( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + SrcTex.Handle, + DstTex.Handle, + FramebufferAttachment.ColorAttachment0, + ClearBufferMask.ColorBufferBit, + true); + } + else if (SrcTex.HasDepth && SrcTex.HasStencil) + { + CopyTextures( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + SrcTex.Handle, + DstTex.Handle, + FramebufferAttachment.DepthStencilAttachment, + ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit, + false); + } + else if (SrcTex.HasDepth) + { + CopyTextures( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + SrcTex.Handle, + DstTex.Handle, + FramebufferAttachment.DepthAttachment, + ClearBufferMask.DepthBufferBit, + false); + } + else if (SrcTex.HasStencil) + { + CopyTextures( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + SrcTex.Handle, + DstTex.Handle, + FramebufferAttachment.StencilAttachment, + ClearBufferMask.StencilBufferBit, + false); + } + else + { + throw new InvalidOperationException(); + } + } + } + + public void GetBufferData(long Key, Action<byte[]> Callback) + { + if (Texture.TryGetImage(Key, out ImageHandler Tex)) + { + byte[] Data = new byte[ImageUtils.GetSize(Tex.Image)]; + + GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); + + GL.GetTexImage( + TextureTarget.Texture2D, + 0, + Tex.PixelFormat, + Tex.PixelType, + Data); + + Callback(Data); + } + } + + public void SetBufferData( + long Key, + int Width, + int Height, + byte[] Buffer) + { + if (Texture.TryGetImage(Key, out ImageHandler Tex)) + { + GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); + + const int Level = 0; + const int Border = 0; + + GL.TexImage2D( + TextureTarget.Texture2D, + Level, + Tex.InternalFormat, + Width, + Height, + Border, + Tex.PixelFormat, + Tex.PixelType, + Buffer); + } + } + + private void EnsureFrameBuffer() + { + if (DummyFrameBuffer == 0) + { + DummyFrameBuffer = GL.GenFramebuffer(); + } + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer); + } + + private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment) + { + if (OldHandle != NewHandle) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FbAttachment, + NewHandle, + 0); + + OldHandle = NewHandle; + } + } + + 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) + { + if (SrcFb == 0) SrcFb = GL.GenFramebuffer(); + if (DstFb == 0) DstFb = GL.GenFramebuffer(); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb); + + GL.FramebufferTexture( + FramebufferTarget.ReadFramebuffer, + Attachment, + SrcTexture, + 0); + + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + Attachment, + DstTexture, + 0); + + if (Color) + { + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + } + + GL.Clear(Mask); + + GL.BlitFramebuffer( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + Mask, + Color ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest); + + EnsureFrameBuffer(); + } + } +}
\ No newline at end of file |
