aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs')
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs497
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