Ryujinx/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
BaronKiko 0973daefa1 Fixed Scissor Test on Intel based GPUs (#595)
* Reworked scissor tests to remove fixme and handle issues with intel gpu's

* Error handling for scissor tests

* Disable strict opengl by default

* Reformatting for JD

* Updated scheme for new property in config

* Fixed typo

* Moved magic value to constant. I liked the magic :(

* Fixed ordering for undertale

* Fixed undertale bug

* Removed strict opengl in favour of required. With this an exception is no longer thrown, just a warning for required extensions

* Uses clamp instead of if's

* Removed evil tabs and no longer used include
2019-03-02 21:50:21 +11:00

549 lines
17 KiB
C#

using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLRenderTarget : IGalRenderTarget
{
private const int NativeWidth = 1280;
private const int NativeHeight = 720;
private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount;
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 int MapCount { get; set; }
public DrawBuffersEnum[] Map { get; private set; }
public long[] Colors { get; private set; }
public long Zeta { get; set; }
public FrameBufferAttachments()
{
Colors = new long[RenderTargetsCount];
Map = new DrawBuffersEnum[RenderTargetsCount];
}
public void Update(FrameBufferAttachments Source)
{
for (int Index = 0; Index < RenderTargetsCount; Index++)
{
Map[Index] = Source.Map[Index];
Colors[Index] = Source.Colors[Index];
}
MapCount = Source.MapCount;
Zeta = Source.Zeta;
}
}
private int[] ColorHandles;
private int ZetaHandle;
private OGLTexture Texture;
private ImageHandler ReadTex;
private Rect Window;
private float[] Viewports;
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 bool FramebufferSrgb { get; set; }
public OGLRenderTarget(OGLTexture Texture)
{
Attachments = new FrameBufferAttachments();
OldAttachments = new FrameBufferAttachments();
ColorHandles = new int[RenderTargetsCount];
Viewports = new float[RenderTargetsCount * 4];
this.Texture = Texture;
Texture.TextureDeleted += TextureDeletionHandler;
}
private void TextureDeletionHandler(object Sender, int Handle)
{
//Texture was deleted, the handle is no longer valid, so
//reset all uses of this handle on a render target.
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
{
if (ColorHandles[Attachment] == Handle)
{
ColorHandles[Attachment] = 0;
}
}
if (ZetaHandle == Handle)
{
ZetaHandle = 0;
}
}
public void Bind()
{
if (DummyFrameBuffer == 0)
{
DummyFrameBuffer = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
ImageHandler CachedImage;
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
{
long Key = Attachments.Colors[Attachment];
int Handle = 0;
if (Key != 0 && Texture.TryGetImageHandler(Key, out CachedImage))
{
Handle = CachedImage.Handle;
}
if (Handle == ColorHandles[Attachment])
{
continue;
}
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.ColorAttachment0 + Attachment,
Handle,
0);
ColorHandles[Attachment] = Handle;
}
if (Attachments.Zeta != 0 && Texture.TryGetImageHandler(Attachments.Zeta, out CachedImage))
{
if (CachedImage.Handle != ZetaHandle)
{
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 InvalidOperationException("Invalid image format \"" + CachedImage.Format + "\" used as Zeta!");
}
ZetaHandle = CachedImage.Handle;
}
}
else if (ZetaHandle != 0)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
ZetaHandle = 0;
}
if (OGLExtension.ViewportArray)
{
GL.ViewportArray(0, RenderTargetsCount, 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.Update(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.Disable(EnableCap.FramebufferSrgb);
GL.BlitFramebuffer(
SrcX0,
SrcY0,
SrcX1,
SrcY1,
DstX0,
DstY0,
DstX1,
DstY1,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
if (FramebufferSrgb)
{
GL.Enable(EnableCap.FramebufferSrgb);
}
}
public void Copy(
GalImage SrcImage,
GalImage DstImage,
long SrcKey,
long DstKey,
int SrcLayer,
int DstLayer,
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);
if (ImageUtils.IsArray(SrcImage.TextureTarget) && SrcLayer > 0)
{
GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0, SrcLayer);
}
else
{
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0);
}
if (ImageUtils.IsArray(DstImage.TextureTarget) && DstLayer > 0)
{
GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0, DstLayer);
}
else
{
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.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 &&
NewImage.Width == OldImage.Width &&
NewImage.Height == OldImage.Height &&
NewImage.Depth == OldImage.Depth &&
NewImage.LayerCount == OldImage.LayerCount &&
NewImage.TextureTarget == OldImage.TextureTarget)
{
return;
}
if (CopyPBO == 0)
{
CopyPBO = GL.GenBuffer();
}
GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyPBO);
//The buffer should be large enough to hold the largest texture.
int BufferSize = Math.Max(ImageUtils.GetSize(OldImage),
ImageUtils.GetSize(NewImage));
GL.BufferData(BufferTarget.PixelPackBuffer, BufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
if (!Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
{
throw new InvalidOperationException();
}
(_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);
TextureTarget Target = ImageUtils.GetTextureTarget(NewImage.TextureTarget);
GL.BindTexture(Target, CachedImage.Handle);
GL.GetTexImage(Target, 0, Format, Type, IntPtr.Zero);
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO);
GL.PixelStore(PixelStoreParameter.UnpackRowLength, OldImage.Width);
Texture.Create(Key, ImageUtils.GetSize(NewImage), NewImage);
GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0);
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);
}
}
}