1344a47c77
* Blit framebuffer without shaders * De-hardcode native size values * Adapt to dehardcoded framebuffers and address feedback * Remove framebuffer rebinding
402 lines
12 KiB
C#
402 lines
12 KiB
C#
using OpenTK;
|
|
using OpenTK.Graphics.OpenGL;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
|
{
|
|
public class OGLFrameBuffer : IGalFrameBuffer
|
|
{
|
|
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 FrameBuffer
|
|
{
|
|
public int Width { get; set; }
|
|
public int Height { get; set; }
|
|
|
|
public int Handle { get; private set; }
|
|
public int RbHandle { get; private set; }
|
|
public int TexHandle { get; private set; }
|
|
|
|
public FrameBuffer(int Width, int Height, bool HasRenderBuffer)
|
|
{
|
|
this.Width = Width;
|
|
this.Height = Height;
|
|
|
|
Handle = GL.GenFramebuffer();
|
|
TexHandle = GL.GenTexture();
|
|
|
|
if (HasRenderBuffer)
|
|
{
|
|
RbHandle = GL.GenRenderbuffer();
|
|
}
|
|
}
|
|
}
|
|
|
|
private const int NativeWidth = 1280;
|
|
private const int NativeHeight = 720;
|
|
|
|
private Dictionary<long, FrameBuffer> Fbs;
|
|
|
|
private Rect Viewport;
|
|
private Rect Window;
|
|
|
|
private FrameBuffer CurrFb;
|
|
private FrameBuffer CurrReadFb;
|
|
|
|
private FrameBuffer RawFb;
|
|
|
|
private bool FlipX;
|
|
private bool FlipY;
|
|
|
|
private int CropTop;
|
|
private int CropLeft;
|
|
private int CropRight;
|
|
private int CropBottom;
|
|
|
|
public OGLFrameBuffer()
|
|
{
|
|
Fbs = new Dictionary<long, FrameBuffer>();
|
|
}
|
|
|
|
public void Create(long Key, int Width, int Height)
|
|
{
|
|
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
|
|
{
|
|
if (Fb.Width != Width ||
|
|
Fb.Height != Height)
|
|
{
|
|
SetupTexture(Fb.TexHandle, Width, Height);
|
|
|
|
Fb.Width = Width;
|
|
Fb.Height = Height;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
Fb = new FrameBuffer(Width, Height, true);
|
|
|
|
SetupTexture(Fb.TexHandle, Width, Height);
|
|
|
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
|
|
|
|
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle);
|
|
|
|
GL.RenderbufferStorage(
|
|
RenderbufferTarget.Renderbuffer,
|
|
RenderbufferStorage.Depth24Stencil8,
|
|
Width,
|
|
Height);
|
|
|
|
GL.FramebufferRenderbuffer(
|
|
FramebufferTarget.Framebuffer,
|
|
FramebufferAttachment.DepthStencilAttachment,
|
|
RenderbufferTarget.Renderbuffer,
|
|
Fb.RbHandle);
|
|
|
|
GL.FramebufferTexture(
|
|
FramebufferTarget.Framebuffer,
|
|
FramebufferAttachment.ColorAttachment0,
|
|
Fb.TexHandle,
|
|
0);
|
|
|
|
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
|
|
|
|
Fbs.Add(Key, Fb);
|
|
}
|
|
|
|
public void Bind(long Key)
|
|
{
|
|
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
|
|
{
|
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
|
|
|
|
CurrFb = Fb;
|
|
}
|
|
}
|
|
|
|
public void BindTexture(long Key, int Index)
|
|
{
|
|
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
|
|
{
|
|
GL.ActiveTexture(TextureUnit.Texture0 + Index);
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
|
|
}
|
|
}
|
|
|
|
public void Set(long Key)
|
|
{
|
|
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
|
|
{
|
|
CurrReadFb = Fb;
|
|
}
|
|
}
|
|
|
|
public void Set(byte[] Data, int Width, int Height)
|
|
{
|
|
if (RawFb == null)
|
|
{
|
|
CreateRawFb(Width, Height);
|
|
}
|
|
|
|
if (RawFb.Width != Width ||
|
|
RawFb.Height != Height)
|
|
{
|
|
SetupTexture(RawFb.TexHandle, Width, Height);
|
|
|
|
RawFb.Width = Width;
|
|
RawFb.Height = Height;
|
|
}
|
|
|
|
GL.ActiveTexture(TextureUnit.Texture0);
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, RawFb.TexHandle);
|
|
|
|
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
|
|
|
|
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data);
|
|
|
|
CurrReadFb = RawFb;
|
|
}
|
|
|
|
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 (CurrReadFb != 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);
|
|
}
|
|
}
|
|
|
|
public void Copy(
|
|
long SrcKey,
|
|
long DstKey,
|
|
int SrcX0,
|
|
int SrcY0,
|
|
int SrcX1,
|
|
int SrcY1,
|
|
int DstX0,
|
|
int DstY0,
|
|
int DstX1,
|
|
int DstY1)
|
|
{
|
|
if (Fbs.TryGetValue(SrcKey, out FrameBuffer SrcFb) &&
|
|
Fbs.TryGetValue(DstKey, out FrameBuffer DstFb))
|
|
{
|
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb.Handle);
|
|
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb.Handle);
|
|
|
|
GL.Clear(ClearBufferMask.ColorBufferBit);
|
|
|
|
GL.BlitFramebuffer(
|
|
SrcX0, SrcY0, SrcX1, SrcY1,
|
|
DstX0, DstY0, DstX1, DstY1,
|
|
ClearBufferMask.ColorBufferBit,
|
|
BlitFramebufferFilter.Linear);
|
|
}
|
|
}
|
|
|
|
public void GetBufferData(long Key, Action<byte[]> Callback)
|
|
{
|
|
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
|
|
{
|
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle);
|
|
|
|
byte[] Data = new byte[Fb.Width * Fb.Height * 4];
|
|
|
|
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
|
|
|
|
GL.ReadPixels(
|
|
0,
|
|
0,
|
|
Fb.Width,
|
|
Fb.Height,
|
|
Format,
|
|
Type,
|
|
Data);
|
|
|
|
Callback(Data);
|
|
}
|
|
}
|
|
|
|
public void SetBufferData(
|
|
long Key,
|
|
int Width,
|
|
int Height,
|
|
GalTextureFormat Format,
|
|
byte[] Buffer)
|
|
{
|
|
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
|
|
{
|
|
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
|
|
|
|
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,
|
|
Width,
|
|
Height,
|
|
Border,
|
|
GlFormat,
|
|
Type,
|
|
Buffer);
|
|
}
|
|
}
|
|
|
|
private void CreateRawFb(int Width, int Height)
|
|
{
|
|
if (RawFb == null)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
private void SetupTexture(int Handle, int Width, int Height)
|
|
{
|
|
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);
|
|
|
|
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
|
|
|
|
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
|
|
|
|
const int Level = 0;
|
|
const int Border = 0;
|
|
|
|
GL.TexImage2D(
|
|
TextureTarget.Texture2D,
|
|
Level,
|
|
InternalFmt,
|
|
Width,
|
|
Height,
|
|
Border,
|
|
Format,
|
|
Type,
|
|
IntPtr.Zero);
|
|
}
|
|
}
|
|
} |