mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-11-23 20:42:02 +00:00
2562ca6c3f
* Simplify render target bindings * Implement multiple viewports * Pack glViewportIndexed calls into a single glViewportArray * Use ARB_viewport_array when available * Cache framebuffer attachments * Use get accessors in OGLExtension * Address feedback
474 lines
No EOL
14 KiB
C#
474 lines
No EOL
14 KiB
C#
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 class FrameBufferAttachments
|
|
{
|
|
public long[] Colors;
|
|
public long Zeta;
|
|
|
|
public int MapCount;
|
|
public DrawBuffersEnum[] Map;
|
|
|
|
public FrameBufferAttachments()
|
|
{
|
|
Colors = new long[RenderTargetsCount];
|
|
|
|
Map = new DrawBuffersEnum[RenderTargetsCount];
|
|
}
|
|
|
|
public void SetAndClear(FrameBufferAttachments Source)
|
|
{
|
|
Zeta = Source.Zeta;
|
|
MapCount = Source.MapCount;
|
|
|
|
Source.Zeta = 0;
|
|
Source.MapCount = 0;
|
|
|
|
for (int i = 0; i < RenderTargetsCount; i++)
|
|
{
|
|
Colors[i] = Source.Colors[i];
|
|
Map[i] = Source.Map[i];
|
|
|
|
Source.Colors[i] = 0;
|
|
Source.Map[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
private const int NativeWidth = 1280;
|
|
private const int NativeHeight = 720;
|
|
|
|
private const int RenderTargetsCount = 8;
|
|
|
|
private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
|
|
|
|
private OGLTexture Texture;
|
|
|
|
private ImageHandler ReadTex;
|
|
|
|
private float[] Viewports;
|
|
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;
|
|
|
|
private FrameBufferAttachments Attachments;
|
|
private FrameBufferAttachments OldAttachments;
|
|
|
|
private int CopyPBO;
|
|
|
|
public OGLRenderTarget(OGLTexture Texture)
|
|
{
|
|
Attachments = new FrameBufferAttachments();
|
|
|
|
OldAttachments = new FrameBufferAttachments();
|
|
|
|
Viewports = new float[RenderTargetsCount * 4];
|
|
|
|
this.Texture = Texture;
|
|
}
|
|
|
|
public void Bind()
|
|
{
|
|
if (DummyFrameBuffer == 0)
|
|
{
|
|
DummyFrameBuffer = GL.GenFramebuffer();
|
|
}
|
|
|
|
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
|
|
|
|
ImageHandler CachedImage;
|
|
|
|
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
|
|
{
|
|
if (Attachments.Colors[Attachment] == OldAttachments.Colors[Attachment])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int Handle = 0;
|
|
|
|
if (Attachments.Colors[Attachment] != 0 &&
|
|
Texture.TryGetImageHandler(Attachments.Colors[Attachment], out CachedImage))
|
|
{
|
|
Handle = CachedImage.Handle;
|
|
}
|
|
|
|
GL.FramebufferTexture(
|
|
FramebufferTarget.DrawFramebuffer,
|
|
FramebufferAttachment.ColorAttachment0 + Attachment,
|
|
Handle,
|
|
0);
|
|
}
|
|
|
|
if (Attachments.Zeta != OldAttachments.Zeta)
|
|
{
|
|
if (Attachments.Zeta != 0 && Texture.TryGetImageHandler(Attachments.Zeta, out CachedImage))
|
|
{
|
|
if (CachedImage.HasDepth && CachedImage.HasStencil)
|
|
{
|
|
GL.FramebufferTexture(
|
|
FramebufferTarget.DrawFramebuffer,
|
|
FramebufferAttachment.DepthStencilAttachment,
|
|
CachedImage.Handle,
|
|
0);
|
|
}
|
|
else if (CachedImage.HasDepth)
|
|
{
|
|
GL.FramebufferTexture(
|
|
FramebufferTarget.DrawFramebuffer,
|
|
FramebufferAttachment.DepthAttachment,
|
|
CachedImage.Handle,
|
|
0);
|
|
|
|
GL.FramebufferTexture(
|
|
FramebufferTarget.DrawFramebuffer,
|
|
FramebufferAttachment.StencilAttachment,
|
|
0,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GL.FramebufferTexture(
|
|
FramebufferTarget.DrawFramebuffer,
|
|
FramebufferAttachment.DepthStencilAttachment,
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
|
|
if (OGLExtension.ViewportArray)
|
|
{
|
|
GL.ViewportArray(0, 8, Viewports);
|
|
}
|
|
else
|
|
{
|
|
GL.Viewport(
|
|
(int)Viewports[0],
|
|
(int)Viewports[1],
|
|
(int)Viewports[2],
|
|
(int)Viewports[3]);
|
|
}
|
|
|
|
if (Attachments.MapCount > 1)
|
|
{
|
|
GL.DrawBuffers(Attachments.MapCount, Attachments.Map);
|
|
}
|
|
else if (Attachments.MapCount == 1)
|
|
{
|
|
GL.DrawBuffer((DrawBufferMode)Attachments.Map[0]);
|
|
}
|
|
else
|
|
{
|
|
GL.DrawBuffer(DrawBufferMode.None);
|
|
}
|
|
|
|
OldAttachments.SetAndClear(Attachments);
|
|
}
|
|
|
|
public void BindColor(long Key, int Attachment)
|
|
{
|
|
Attachments.Colors[Attachment] = Key;
|
|
}
|
|
|
|
public void UnbindColor(int Attachment)
|
|
{
|
|
Attachments.Colors[Attachment] = 0;
|
|
}
|
|
|
|
public void BindZeta(long Key)
|
|
{
|
|
Attachments.Zeta = Key;
|
|
}
|
|
|
|
public void UnbindZeta()
|
|
{
|
|
Attachments.Zeta = 0;
|
|
}
|
|
|
|
public void Present(long Key)
|
|
{
|
|
Texture.TryGetImageHandler(Key, out ReadTex);
|
|
}
|
|
|
|
public void SetMap(int[] Map)
|
|
{
|
|
if (Map != null)
|
|
{
|
|
Attachments.MapCount = Map.Length;
|
|
|
|
for (int Attachment = 0; Attachment < Attachments.MapCount; Attachment++)
|
|
{
|
|
Attachments.Map[Attachment] = DrawBuffersEnum.ColorAttachment0 + Map[Attachment];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Attachments.MapCount = 0;
|
|
}
|
|
}
|
|
|
|
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 Attachment, int X, int Y, int Width, int Height)
|
|
{
|
|
int Offset = Attachment * 4;
|
|
|
|
Viewports[Offset + 0] = X;
|
|
Viewports[Offset + 1] = Y;
|
|
Viewports[Offset + 2] = Width;
|
|
Viewports[Offset + 3] = 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;
|
|
|
|
GL.Viewport(0, 0, Window.Width, Window.Height);
|
|
|
|
if (SrcFb == 0)
|
|
{
|
|
SrcFb = GL.GenFramebuffer();
|
|
}
|
|
|
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
|
|
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
|
|
|
|
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0);
|
|
|
|
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
|
|
|
|
GL.Clear(ClearBufferMask.ColorBufferBit);
|
|
|
|
GL.BlitFramebuffer(
|
|
SrcX0, SrcY0, SrcX1, SrcY1,
|
|
DstX0, DstY0, DstX1, DstY1,
|
|
ClearBufferMask.ColorBufferBit,
|
|
BlitFramebufferFilter.Linear);
|
|
}
|
|
|
|
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.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) &&
|
|
Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex))
|
|
{
|
|
if (SrcTex.HasColor != DstTex.HasColor ||
|
|
SrcTex.HasDepth != DstTex.HasDepth ||
|
|
SrcTex.HasStencil != DstTex.HasStencil)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
if (SrcFb == 0)
|
|
{
|
|
SrcFb = GL.GenFramebuffer();
|
|
}
|
|
|
|
if (DstFb == 0)
|
|
{
|
|
DstFb = GL.GenFramebuffer();
|
|
}
|
|
|
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
|
|
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb);
|
|
|
|
FramebufferAttachment Attachment = GetAttachment(SrcTex);
|
|
|
|
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0);
|
|
GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0);
|
|
|
|
BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest;
|
|
|
|
if (SrcTex.HasColor)
|
|
{
|
|
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
|
|
|
|
Filter = BlitFramebufferFilter.Linear;
|
|
}
|
|
|
|
ClearBufferMask Mask = GetClearMask(SrcTex);
|
|
|
|
GL.Clear(Mask);
|
|
|
|
GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter);
|
|
}
|
|
}
|
|
|
|
public void Reinterpret(long Key, GalImage NewImage)
|
|
{
|
|
if (!Texture.TryGetImage(Key, out GalImage OldImage))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (NewImage.Format == OldImage.Format)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CopyPBO == 0)
|
|
{
|
|
CopyPBO = GL.GenBuffer();
|
|
}
|
|
|
|
GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyPBO);
|
|
|
|
GL.BufferData(BufferTarget.PixelPackBuffer, Math.Max(ImageUtils.GetSize(OldImage), ImageUtils.GetSize(NewImage)), IntPtr.Zero, BufferUsageHint.StreamCopy);
|
|
|
|
if (!Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
(_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
|
|
|
|
GL.GetTexImage(TextureTarget.Texture2D, 0, Format, Type, IntPtr.Zero);
|
|
|
|
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
|
|
|
|
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO);
|
|
|
|
Texture.Create(Key, ImageUtils.GetSize(NewImage), NewImage);
|
|
|
|
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
|
|
}
|
|
|
|
private static FramebufferAttachment GetAttachment(ImageHandler CachedImage)
|
|
{
|
|
if (CachedImage.HasColor)
|
|
{
|
|
return FramebufferAttachment.ColorAttachment0;
|
|
}
|
|
else if (CachedImage.HasDepth && CachedImage.HasStencil)
|
|
{
|
|
return FramebufferAttachment.DepthStencilAttachment;
|
|
}
|
|
else if (CachedImage.HasDepth)
|
|
{
|
|
return FramebufferAttachment.DepthAttachment;
|
|
}
|
|
else if (CachedImage.HasStencil)
|
|
{
|
|
return FramebufferAttachment.StencilAttachment;
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
private static ClearBufferMask GetClearMask(ImageHandler CachedImage)
|
|
{
|
|
return (CachedImage.HasColor ? ClearBufferMask.ColorBufferBit : 0) |
|
|
(CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) |
|
|
(CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0);
|
|
}
|
|
}
|
|
} |