Use textures for framebuffers and split color and zeta framebuffers

This commit is contained in:
ReinUsesLisp 2018-08-05 20:30:25 -03:00
parent a11293b793
commit b723bd5e4a
10 changed files with 557 additions and 218 deletions

View file

@ -19,7 +19,7 @@
RG32Sint = 0xcc,
RG32Uint = 0xcd,
RGBX16Float = 0xce,
BGRA8Unorm = 0x0cf,
BGRA8Unorm = 0xcf,
BGRA8Srgb = 0xd0,
RGB10A2Unorm = 0xd1,
RGB10A2Uint = 0xd2,

View file

@ -4,9 +4,17 @@ namespace Ryujinx.Graphics.Gal
{
public interface IGalFrameBuffer
{
void Create(long Key, int Width, int Height);
void CreateColor(long Key, int Width, int Height, GalFrameBufferFormat Format);
void Bind(long Key);
void BindColor(long Key, int Attachment);
void UnbindColor(int Attachment);
void CreateZeta(long Key, int Width, int Height, GalZetaFormat Format);
void BindZeta(long Key);
void UnbindZeta();
void BindTexture(long Key, int Index);
@ -40,7 +48,6 @@ namespace Ryujinx.Graphics.Gal
long Key,
int Width,
int Height,
GalTextureFormat Format,
byte[] Buffer);
}
}

View file

@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Gal
void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red, float Green, float Blue, float Alpha,
float Depth,
int Stencil);

View file

@ -0,0 +1,48 @@
using OpenTK.Graphics.OpenGL;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLBlend : IGalBlend
{
public void Enable()
{
GL.Enable(EnableCap.Blend);
}
public void Disable()
{
GL.Disable(EnableCap.Blend);
}
public void Set(
GalBlendEquation Equation,
GalBlendFactor FuncSrc,
GalBlendFactor FuncDst)
{
GL.BlendEquation(OGLEnumConverter.GetBlendEquation(Equation));
GL.BlendFunc(
OGLEnumConverter.GetBlendFactor(FuncSrc),
OGLEnumConverter.GetBlendFactor(FuncDst));
}
public void SetSeparate(
GalBlendEquation EquationRgb,
GalBlendEquation EquationAlpha,
GalBlendFactor FuncSrcRgb,
GalBlendFactor FuncDstRgb,
GalBlendFactor FuncSrcAlpha,
GalBlendFactor FuncDstAlpha)
{
GL.BlendEquationSeparate(
OGLEnumConverter.GetBlendEquation(EquationRgb),
OGLEnumConverter.GetBlendEquation(EquationAlpha));
GL.BlendFuncSeparate(
(BlendingFactorSrc)OGLEnumConverter.GetBlendFactor(FuncSrcRgb),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(FuncDstRgb),
(BlendingFactorSrc)OGLEnumConverter.GetBlendFactor(FuncSrcAlpha),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(FuncDstAlpha));
}
}
}

View file

@ -125,6 +125,75 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentException(nameof(Type));
}
public static PixelInternalFormat GetFrameBufferInternalFormat(GalFrameBufferFormat Format)
{
switch (Format)
{
//Sometimes it's not set, use a safe format
case 0: return PixelInternalFormat.Rgba8;
case GalFrameBufferFormat.RGBA32Float: return PixelInternalFormat.Rgba32f;
case GalFrameBufferFormat.RGBA32Sint: return PixelInternalFormat.Rgba32i;
case GalFrameBufferFormat.RGBA32Uint: return PixelInternalFormat.Rgba32ui;
case GalFrameBufferFormat.RGBA16Unorm: return PixelInternalFormat.Rgba16;
case GalFrameBufferFormat.RGBA16Snorm: return PixelInternalFormat.Rgba16Snorm;
case GalFrameBufferFormat.RGBA16Sint: return PixelInternalFormat.Rgba16i;
case GalFrameBufferFormat.RGBA16Uint: return PixelInternalFormat.Rgba16ui;
case GalFrameBufferFormat.RGBA16Float: return PixelInternalFormat.Rgba16f;
case GalFrameBufferFormat.RG32Float: return PixelInternalFormat.Rg32f;
case GalFrameBufferFormat.RG32Sint: return PixelInternalFormat.Rg32i;
case GalFrameBufferFormat.RG32Uint: return PixelInternalFormat.Rg32ui;
case GalFrameBufferFormat.RGB10A2Unorm: return PixelInternalFormat.Rgb10A2;
case GalFrameBufferFormat.RGB10A2Uint: return PixelInternalFormat.Rgb10A2ui;
case GalFrameBufferFormat.RGBA8Unorm: return PixelInternalFormat.Rgba8;
case GalFrameBufferFormat.RGBA8Srgb: return PixelInternalFormat.Srgb8;
case GalFrameBufferFormat.RG16Snorm: return PixelInternalFormat.Rg16Snorm;
case GalFrameBufferFormat.R11G11B10Float: return PixelInternalFormat.R11fG11fB10f;
case GalFrameBufferFormat.R32Float: return PixelInternalFormat.R32f;
case GalFrameBufferFormat.R16Float: return PixelInternalFormat.R16f;
case GalFrameBufferFormat.R8Unorm: return PixelInternalFormat.R8;
case GalFrameBufferFormat.R8Snorm: return PixelInternalFormat.R8Snorm;
case GalFrameBufferFormat.R8Sint: return PixelInternalFormat.R8i;
case GalFrameBufferFormat.R8Uint: return PixelInternalFormat.R8ui;
}
throw new NotImplementedException(Format.ToString());
}
public static (PixelFormat Format, PixelType Type) GetFrameBufferFormat(GalFrameBufferFormat Format)
{
switch (Format)
{
case 0: return (PixelFormat.Rgba, PixelType.UnsignedByte);
case GalFrameBufferFormat.RGBA32Float: return (PixelFormat.Rgba, PixelType.Float);
case GalFrameBufferFormat.RGBA32Sint: return (PixelFormat.Rgba, PixelType.Int);
case GalFrameBufferFormat.RGBA32Uint: return (PixelFormat.Rgba, PixelType.UnsignedInt);
case GalFrameBufferFormat.RGBA16Unorm: return (PixelFormat.Rgba, PixelType.UnsignedShort);
case GalFrameBufferFormat.RGBA16Snorm: return (PixelFormat.Rgba, PixelType.Short);
case GalFrameBufferFormat.RGBA16Sint: return (PixelFormat.Rgba, PixelType.Short);
case GalFrameBufferFormat.RGBA16Uint: return (PixelFormat.Rgba, PixelType.UnsignedShort);
case GalFrameBufferFormat.RGBA16Float: return (PixelFormat.Rgba, PixelType.HalfFloat);
case GalFrameBufferFormat.RG32Float: return (PixelFormat.Rg, PixelType.Float);
case GalFrameBufferFormat.RG32Sint: return (PixelFormat.Rg, PixelType.Int);
case GalFrameBufferFormat.RG32Uint: return (PixelFormat.Rg, PixelType.UnsignedInt);
case GalFrameBufferFormat.RGB10A2Unorm: return (PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalFrameBufferFormat.RGB10A2Uint: return (PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalFrameBufferFormat.RGBA8Unorm: return (PixelFormat.Rgba, PixelType.UnsignedByte);
case GalFrameBufferFormat.RGBA8Srgb: return (PixelFormat.Rgba, PixelType.UnsignedByte);
case GalFrameBufferFormat.RG16Snorm: return (PixelFormat.Rg, PixelType.Short);
case GalFrameBufferFormat.R11G11B10Float: return (PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev);
case GalFrameBufferFormat.R32Float: return (PixelFormat.Red, PixelType.Float);
case GalFrameBufferFormat.R16Float: return (PixelFormat.Red, PixelType.HalfFloat);
case GalFrameBufferFormat.R8Unorm: return (PixelFormat.Red, PixelType.UnsignedByte);
case GalFrameBufferFormat.R8Snorm: return (PixelFormat.Red, PixelType.Byte);
case GalFrameBufferFormat.R8Sint: return (PixelFormat.Red, PixelType.Byte);
case GalFrameBufferFormat.R8Uint: return (PixelFormat.Red, PixelType.UnsignedByte);
}
throw new NotImplementedException(Format.ToString());
}
public static (PixelFormat, PixelType) GetTextureFormat(GalTextureFormat Format)
{
switch (Format)

View file

@ -16,50 +16,163 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public Rect(int X, int Y, int Width, int Height)
{
this.X = X;
this.Y = Y;
this.Width = Width;
this.X = X;
this.Y = Y;
this.Width = Width;
this.Height = Height;
}
}
private class FrameBuffer
private class Texture
{
public int Width { get; set; }
public int Height { get; set; }
public int Width { get; private set; }
public int Height { get; private set; }
public int Handle { get; private set; }
public int RbHandle { get; private set; }
public int TexHandle { get; private set; }
public PixelInternalFormat InternalFormat { get; private set; }
public PixelFormat Format { get; private set; }
public PixelType Type { get; private set; }
public FrameBuffer(int Width, int Height, bool HasRenderBuffer)
public int Handle { get; private set; }
private bool Initialized;
public Texture()
{
this.Width = Width;
this.Height = Height;
Handle = GL.GenTexture();
}
Handle = GL.GenFramebuffer();
TexHandle = GL.GenTexture();
if (HasRenderBuffer)
public void EnsureSetup(
int Width,
int Height,
PixelInternalFormat InternalFormat,
PixelFormat Format,
PixelType Type)
{
if (!Initialized ||
this.Width != Width ||
this.Height != Height ||
this.InternalFormat != InternalFormat)
{
RbHandle = GL.GenRenderbuffer();
int CopyBuffer = 0;
bool ChangingFormat = Initialized && this.InternalFormat != InternalFormat;
GL.BindTexture(TextureTarget.Texture2D, Handle);
if (ChangingFormat)
{
CopyBuffer = GL.GenBuffer();
GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer);
int MaxWidth = Math.Max(Width, this.Width);
int MaxHeight = Math.Max(Height, this.Height);
//TODO: Dehardcode size number
GL.BufferData(BufferTarget.PixelPackBuffer, MaxWidth * MaxHeight * MaxBpp, IntPtr.Zero, BufferUsageHint.StaticCopy);
GL.GetTexImage(TextureTarget.Texture2D, 0, this.Format, this.Type, IntPtr.Zero);
GL.DeleteTexture(Handle);
Handle = GL.GenTexture();
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);
const int Level = 0;
const int Border = 0;
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFormat,
Width,
Height,
Border,
Format,
Type,
IntPtr.Zero);
if (ChangingFormat)
{
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
GL.DeleteBuffer(CopyBuffer);
}
this.Width = Width;
this.Height = Height;
this.InternalFormat = InternalFormat;
this.Format = Format;
this.Type = Type;
Initialized = true;
}
}
public void EnsureSetup(int Width, int Height, GalFrameBufferFormat Format)
{
//TODO: Convert color format
EnsureSetup(
Width,
Height,
PixelInternalFormat.Rgba8,
PixelFormat.Rgba,
PixelType.UnsignedByte);
}
public void EnsureSetup(int Width, int Height, GalZetaFormat Format)
{
//TODO: Convert zeta format
EnsureSetup(
Width,
Height,
PixelInternalFormat.Depth24Stencil8,
PixelFormat.DepthStencil,
PixelType.UnsignedInt248);
}
}
private static readonly DrawBuffersEnum[] DrawBuffers = new DrawBuffersEnum[]
{
DrawBuffersEnum.ColorAttachment0,
DrawBuffersEnum.ColorAttachment1,
DrawBuffersEnum.ColorAttachment2,
DrawBuffersEnum.ColorAttachment3,
DrawBuffersEnum.ColorAttachment4,
DrawBuffersEnum.ColorAttachment5,
DrawBuffersEnum.ColorAttachment6,
DrawBuffersEnum.ColorAttachment7,
};
private const int NativeWidth = 1280;
private const int NativeHeight = 720;
private Dictionary<long, FrameBuffer> Fbs;
//TODO: Use a variable value here
private const int MaxBpp = 16;
private const GalTextureFormat RawFormat = GalTextureFormat.A8B8G8R8;
private Dictionary<long, Texture> ColorTextures;
private Dictionary<long, Texture> ZetaTextures;
private Texture RawTex;
private Texture ReadTex;
private Rect Viewport;
private Rect Window;
private FrameBuffer CurrFb;
private FrameBuffer CurrReadFb;
private FrameBuffer RawFb;
private bool FlipX;
private bool FlipY;
@ -68,111 +181,137 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private int CropRight;
private int CropBottom;
private int DummyFrameBuffer;
private int SrcFb;
private int DstFb;
public OGLFrameBuffer()
{
Fbs = new Dictionary<long, FrameBuffer>();
ColorTextures = new Dictionary<long, Texture>();
ZetaTextures = new Dictionary<long, Texture>();
}
public void Create(long Key, int Width, int Height)
public void CreateColor(long Key, int Width, int Height, GalFrameBufferFormat Format)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
if (!ColorTextures.TryGetValue(Key, out Texture Tex))
{
if (Fb.Width != Width ||
Fb.Height != Height)
{
SetupTexture(Fb.TexHandle, Width, Height);
Tex = new Texture();
Fb.Width = Width;
Fb.Height = Height;
}
return;
ColorTextures.Add(Key, Tex);
}
Fb = new FrameBuffer(Width, Height, true);
Tex.EnsureSetup(Width, Height, Format);
}
SetupTexture(Fb.TexHandle, Width, Height);
public void BindColor(long Key, int Attachment)
{
if (ColorTextures.TryGetValue(Key, out Texture Tex))
{
EnsureFrameBuffer();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferAttachment.ColorAttachment0 + Attachment,
Tex.Handle,
0);
}
else
{
UnbindColor(Attachment);
}
}
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle);
GL.RenderbufferStorage(
RenderbufferTarget.Renderbuffer,
RenderbufferStorage.Depth24Stencil8,
Width,
Height);
GL.FramebufferRenderbuffer(
FramebufferTarget.Framebuffer,
FramebufferAttachment.DepthStencilAttachment,
RenderbufferTarget.Renderbuffer,
Fb.RbHandle);
public void UnbindColor(int Attachment)
{
EnsureFrameBuffer();
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferAttachment.ColorAttachment0,
Fb.TexHandle,
FramebufferAttachment.ColorAttachment0 + Attachment,
0,
0);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
Fbs.Add(Key, Fb);
}
public void Bind(long Key)
public void CreateZeta(long Key, int Width, int Height, GalZetaFormat Format)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
if (!ZetaTextures.TryGetValue(Key, out Texture Tex))
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
Tex = new Texture();
CurrFb = Fb;
ZetaTextures.Add(Key, Tex);
}
Tex.EnsureSetup(Width, Height, Format);
}
public void BindZeta(long Key)
{
if (ZetaTextures.TryGetValue(Key, out Texture Tex))
{
EnsureFrameBuffer();
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferAttachment.DepthStencilAttachment,
Tex.Handle,
0);
}
else
{
UnbindZeta();
}
}
public void UnbindZeta()
{
EnsureFrameBuffer();
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
}
public void BindTexture(long Key, int Index)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
Texture Tex;
if (ColorTextures.TryGetValue(Key, out Tex) ||
ZetaTextures.TryGetValue(Key, out Tex))
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
}
}
public void Set(long Key)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
if (ColorTextures.TryGetValue(Key, out Texture Tex))
{
CurrReadFb = Fb;
ReadTex = Tex;
}
}
public void Set(byte[] Data, int Width, int Height)
{
if (RawFb == null)
if (RawTex == null)
{
CreateRawFb(Width, Height);
RawTex = new Texture();
}
if (RawFb.Width != Width ||
RawFb.Height != Height)
{
SetupTexture(RawFb.TexHandle, Width, Height);
RawTex.EnsureSetup(Width, Height, PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
RawFb.Width = Width;
RawFb.Height = Height;
}
GL.BindTexture(TextureTarget.Texture2D, RawTex.Handle);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, RawFb.TexHandle);
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(RawFormat);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data);
CurrReadFb = RawFb;
ReadTex = RawTex;
}
public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom)
@ -209,60 +348,71 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Render()
{
if (CurrReadFb != null)
if (ReadTex == 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);
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(
@ -277,39 +427,61 @@ namespace Ryujinx.Graphics.Gal.OpenGL
int DstX1,
int DstY1)
{
if (Fbs.TryGetValue(SrcKey, out FrameBuffer SrcFb) &&
Fbs.TryGetValue(DstKey, out FrameBuffer DstFb))
bool Found = false;
if (ColorTextures.TryGetValue(SrcKey, out Texture SrcTex) &&
ColorTextures.TryGetValue(DstKey, out Texture DstTex))
{
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb.Handle);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb.Handle);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.BlitFramebuffer(
CopyTextures(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
SrcTex.Handle,
DstTex.Handle,
FramebufferAttachment.ColorAttachment0,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
true);
Found = true;
}
}
if (ZetaTextures.TryGetValue(SrcKey, out Texture ZetaSrcTex) &&
ZetaTextures.TryGetValue(DstKey, out Texture ZetaDstTex))
{
CopyTextures(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
ZetaSrcTex.Handle,
ZetaDstTex.Handle,
FramebufferAttachment.DepthStencilAttachment,
ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit,
false);
Found = true;
}
if (Found)
{
EnsureFrameBuffer();
}
}
public void GetBufferData(long Key, Action<byte[]> Callback)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
Texture Tex;
if (ColorTextures.TryGetValue(Key, out Tex) ||
ZetaTextures.TryGetValue(Key, out Tex))
{
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle);
//Note: Change this value when framebuffer sizes are dehardcoded
byte[] Data = new byte[Tex.Width * Tex.Height * MaxBpp];
byte[] Data = new byte[Fb.Width * Fb.Height * 4];
GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
GL.ReadPixels(
GL.GetTexImage(
TextureTarget.Texture2D,
0,
0,
Fb.Width,
Fb.Height,
Format,
Type,
Tex.Format,
Tex.Type,
Data);
Callback(Data);
@ -320,83 +492,88 @@ namespace Ryujinx.Graphics.Gal.OpenGL
long Key,
int Width,
int Height,
GalTextureFormat Format,
byte[] Buffer)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
Texture Tex;
if (ColorTextures.TryGetValue(Key, out Tex) ||
ZetaTextures.TryGetValue(Key, out Tex))
{
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
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,
Tex.InternalFormat,
Width,
Height,
Border,
GlFormat,
Type,
Tex.Format,
Tex.Type,
Buffer);
}
}
private void CreateRawFb(int Width, int Height)
private void EnsureFrameBuffer()
{
if (RawFb == null)
if (DummyFrameBuffer == 0)
{
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);
DummyFrameBuffer = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.Framebuffer, DummyFrameBuffer);
GL.DrawBuffers(8, DrawBuffers);
}
private void SetupTexture(int Handle, int Width, int Height)
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)
{
GL.BindTexture(TextureTarget.Texture2D, Handle);
if (SrcFb == 0) SrcFb = GL.GenFramebuffer();
if (DstFb == 0) DstFb = GL.GenFramebuffer();
const int MinFilter = (int)TextureMinFilter.Linear;
const int MagFilter = (int)TextureMagFilter.Linear;
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
GL.FramebufferTexture(
FramebufferTarget.ReadFramebuffer,
Attachment,
SrcTexture,
0);
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
Attachment,
DstTexture,
0);
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
if (Color)
{
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
}
const int Level = 0;
const int Border = 0;
GL.Clear(Mask);
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Width,
Height,
Border,
Format,
Type,
IntPtr.Zero);
GL.BlitFramebuffer(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
Mask,
Color ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest);
}
}
}

View file

@ -45,17 +45,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red, float Green, float Blue, float Alpha,
float Depth,
int Stencil)
{
ClearBufferMask Mask = ClearBufferMask.ColorBufferBit;
//TODO: Handle attachment
GL.ColorMask(
Flags.HasFlag(GalClearBufferFlags.ColorRed),
Flags.HasFlag(GalClearBufferFlags.ColorGreen),
Flags.HasFlag(GalClearBufferFlags.ColorBlue),
Flags.HasFlag(GalClearBufferFlags.ColorAlpha));
ClearBufferMask Mask = ClearBufferMask.ColorBufferBit;
if (Flags.HasFlag(GalClearBufferFlags.Depth))
{
@ -67,6 +64,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Mask |= ClearBufferMask.StencilBufferBit;
}
GL.ColorMask(
Flags.HasFlag(GalClearBufferFlags.ColorRed),
Flags.HasFlag(GalClearBufferFlags.ColorGreen),
Flags.HasFlag(GalClearBufferFlags.ColorBlue),
Flags.HasFlag(GalClearBufferFlags.ColorAlpha));
GL.ClearColor(Red, Green, Blue, Alpha);
GL.ClearDepth(Depth);

View file

@ -154,16 +154,12 @@ namespace Ryujinx.HLE.Gpu.Engines
}
else if (IsDstFb)
{
//Texture -> Frame Buffer copy.
const GalTextureFormat Format = GalTextureFormat.A8B8G8R8;
byte[] Buffer = TextureReader.Read(Vmm, SrcTexture());
Gpu.Renderer.FrameBuffer.SetBufferData(
DstKey,
DstWidth,
DstHeight,
Format,
Buffer);
}
else

View file

@ -104,6 +104,8 @@ namespace Ryujinx.HLE.Gpu.Engines
SetFrameBuffer(Vmm, 0);
SetZeta(Vmm);
long[] Keys = UploadShaders(Vmm);
Gpu.Renderer.Shader.BindProgram();
@ -152,6 +154,7 @@ namespace Ryujinx.HLE.Gpu.Engines
Gpu.Renderer.Rasterizer.ClearBuffers(
Flags,
FbIndex,
Red, Green, Blue, Alpha,
Depth,
Stencil);
@ -161,13 +164,22 @@ namespace Ryujinx.HLE.Gpu.Engines
{
long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
int Format = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10);
if (VA == 0 || Format == 0)
{
Gpu.Renderer.FrameBuffer.UnbindColor(FbIndex);
return;
}
long Key = Vmm.GetPhysicalAddress(VA);
FrameBuffers.Add(Key);
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 4);
float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 4);
@ -180,12 +192,37 @@ namespace Ryujinx.HLE.Gpu.Engines
int VpW = (int)(TX + MathF.Abs(SX)) - VpX;
int VpH = (int)(TY + MathF.Abs(SY)) - VpY;
Gpu.Renderer.FrameBuffer.Create(Key, Width, Height);
Gpu.Renderer.FrameBuffer.Bind(Key);
Gpu.Renderer.FrameBuffer.CreateColor(Key, Width, Height, (GalFrameBufferFormat)Format);
Gpu.Renderer.FrameBuffer.BindColor(Key, FbIndex);
Gpu.Renderer.FrameBuffer.SetViewport(VpX, VpY, VpW, VpH);
}
private void SetZeta(NvGpuVmm Vmm)
{
long ZA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress);
int Format = ReadRegister(NvGpuEngine3dReg.ZetaFormat);
bool ZetaEnable = (ReadRegister(NvGpuEngine3dReg.ZetaEnable) & 1) != 0;
if (ZA == 0 || Format == 0 || !ZetaEnable)
{
Gpu.Renderer.FrameBuffer.UnbindZeta();
return;
}
long Key = Vmm.GetPhysicalAddress(ZA);
int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz);
int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert);
Gpu.Renderer.FrameBuffer.CreateZeta(Key, Width, Height, (GalZetaFormat)Format);
Gpu.Renderer.FrameBuffer.BindZeta(Key);
}
private long[] UploadShaders(NvGpuVmm Vmm)
{
long[] Keys = new long[5];

View file

@ -51,6 +51,7 @@ namespace Ryujinx.HLE.Gpu.Engines
StencilFrontFuncMask = 0x4e6,
StencilFrontMask = 0x4e7,
VertexArrayElemBase = 0x50d,
ZetaEnable = 0x54e,
TexHeaderPoolOffset = 0x55d,
TexSamplerPoolOffset = 0x557,
StencilTwoSideEnable = 0x565,