Share images between framebuffers and textures

This commit is contained in:
ReinUsesLisp 2018-08-11 01:44:25 -03:00
parent 65c7842d6d
commit 5ea1382aff
9 changed files with 211 additions and 146 deletions

View file

@ -4,14 +4,10 @@ namespace Ryujinx.Graphics.Gal
{
public interface IGalFrameBuffer
{
void CreateColor(long Key, int Width, int Height, GalFrameBufferFormat Format);
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();

View file

@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Gal
void Create(long Key, byte[] Data, GalImage Image);
void CreateFb(long Key, long Size, GalImage Image);
bool TryGetCachedTexture(long Key, long DataSize, out GalImage Image);
void Bind(long Key, int Index);

View file

@ -50,12 +50,22 @@ namespace Ryujinx.Graphics.Gal
public static GalImageFormat ConvertFrameBuffer(GalFrameBufferFormat Format)
{
switch (Format)
{
case GalFrameBufferFormat.R32Float: return GalImageFormat.R32;
}
//Stubbed.
return GalImageFormat.A8B8G8R8;
}
public static GalImageFormat ConvertZeta(GalZetaFormat Format)
{
switch (Format)
{
case GalZetaFormat.Z32Float: return GalImageFormat.ZF32;
}
//Stubbed.
return GalImageFormat.Z24S8;
}

View file

@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLFrameBuffer : IGalFrameBuffer
class OGLFrameBuffer : IGalFrameBuffer
{
private struct Rect
{
@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
private static readonly DrawBuffersEnum[] DrawBuffers = new DrawBuffersEnum[]
{
DrawBuffersEnum.ColorAttachment0,
@ -42,8 +40,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8;
private Dictionary<long, TCE> ColorTextures;
private Dictionary<long, TCE> ZetaTextures;
private OGLTexture Texture;
private TCE RawTex;
private TCE ReadTex;
@ -64,35 +61,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private int SrcFb;
private int DstFb;
public OGLFrameBuffer()
public OGLFrameBuffer(OGLTexture Texture)
{
ColorTextures = new Dictionary<long, TCE>();
ZetaTextures = new Dictionary<long, TCE>();
}
public void CreateColor(long Key, int Width, int Height, GalFrameBufferFormat Format)
{
if (!ColorTextures.TryGetValue(Key, out TCE Tex))
{
Tex = new TCE();
ColorTextures.Add(Key, Tex);
}
GalImageFormat ImageFormat = ImageFormatConverter.ConvertFrameBuffer(Format);
Tex.EnsureSetup(new GalImage(Width, Height, ImageFormat));
this.Texture = Texture;
}
public void BindColor(long Key, int Attachment)
{
if (ColorTextures.TryGetValue(Key, out TCE Tex))
if (Texture.TryGetTCE(Key, out TCE Tex))
{
EnsureFrameBuffer();
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.ColorAttachment0 + Attachment,
Tex.Handle,
0);
@ -108,37 +89,58 @@ namespace Ryujinx.Graphics.Gal.OpenGL
EnsureFrameBuffer();
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.ColorAttachment0 + Attachment,
0,
0);
}
public void CreateZeta(long Key, int Width, int Height, GalZetaFormat Format)
{
if (!ZetaTextures.TryGetValue(Key, out TCE Tex))
{
Tex = new TCE();
ZetaTextures.Add(Key, Tex);
}
GalImageFormat ImageFormat = ImageFormatConverter.ConvertZeta(Format);
Tex.EnsureSetup(new GalImage(Width, Height, ImageFormat));
}
public void BindZeta(long Key)
{
if (ZetaTextures.TryGetValue(Key, out TCE Tex))
if (Texture.TryGetTCE(Key, out TCE Tex))
{
EnsureFrameBuffer();
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferAttachment.DepthStencilAttachment,
Tex.Handle,
0);
if (Tex.HasDepth && Tex.HasStencil)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
Tex.Handle,
0);
}
else if (Tex.HasDepth)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthAttachment,
Tex.Handle,
0);
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.StencilAttachment,
0,
0);
}
else if (Tex.HasStencil)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthAttachment,
Tex.Handle,
0);
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.StencilAttachment,
0,
0);
}
else
{
throw new InvalidOperationException();
}
}
else
{
@ -151,7 +153,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
EnsureFrameBuffer();
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
@ -159,10 +161,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void BindTexture(long Key, int Index)
{
TCE Tex;
if (ColorTextures.TryGetValue(Key, out Tex) ||
ZetaTextures.TryGetValue(Key, out Tex))
if (Texture.TryGetTCE(Key, out TCE Tex))
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
@ -172,7 +171,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Set(long Key)
{
if (ColorTextures.TryGetValue(Key, out TCE Tex))
if (Texture.TryGetTCE(Key, out TCE Tex))
{
ReadTex = Tex;
}
@ -307,50 +306,72 @@ namespace Ryujinx.Graphics.Gal.OpenGL
int DstX1,
int DstY1)
{
bool Found = false;
if (ColorTextures.TryGetValue(SrcKey, out TCE SrcTex) &&
ColorTextures.TryGetValue(DstKey, out TCE DstTex))
if (Texture.TryGetTCE(SrcKey, out TCE SrcTex) &&
Texture.TryGetTCE(DstKey, out TCE DstTex))
{
CopyTextures(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
SrcTex.Handle,
DstTex.Handle,
FramebufferAttachment.ColorAttachment0,
ClearBufferMask.ColorBufferBit,
true);
if (SrcTex.HasColor != DstTex.HasColor ||
SrcTex.HasDepth != DstTex.HasDepth ||
SrcTex.HasStencil != DstTex.HasStencil)
{
throw new NotImplementedException();
}
Found = true;
}
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();
}
if (ZetaTextures.TryGetValue(SrcKey, out TCE ZetaSrcTex) &&
ZetaTextures.TryGetValue(DstKey, out TCE 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)
{
TCE Tex;
if (ColorTextures.TryGetValue(Key, out Tex) ||
ZetaTextures.TryGetValue(Key, out Tex))
if (Texture.TryGetTCE(Key, out TCE Tex))
{
byte[] Data = new byte[Tex.Width * Tex.Height * TCE.MaxBpp];
@ -373,10 +394,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
int Height,
byte[] Buffer)
{
TCE Tex;
if (ColorTextures.TryGetValue(Key, out Tex) ||
ZetaTextures.TryGetValue(Key, out Tex))
if (Texture.TryGetTCE(Key, out TCE Tex))
{
GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
@ -403,7 +421,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
DummyFrameBuffer = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.Framebuffer, DummyFrameBuffer);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
GL.DrawBuffers(8, DrawBuffers);
}

View file

@ -50,33 +50,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL
float Depth,
int Stencil)
{
//TODO: Handle attachment
ClearBufferMask Mask = ClearBufferMask.ColorBufferBit;
if (Flags.HasFlag(GalClearBufferFlags.Depth))
{
Mask |= ClearBufferMask.DepthBufferBit;
}
if (Flags.HasFlag(GalClearBufferFlags.Stencil))
{
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.ClearBuffer(ClearBuffer.Color, Attachment, new float[] { Red, Green, Blue, Alpha });
GL.ClearDepth(Depth);
if (Flags.HasFlag(GalClearBufferFlags.Depth))
{
GL.ClearBuffer(ClearBuffer.Depth, 0, ref Depth);
}
GL.ClearStencil(Stencil);
GL.Clear(Mask);
if (Flags.HasFlag(GalClearBufferFlags.Stencil))
{
GL.ClearBuffer(ClearBuffer.Stencil, 0, ref Stencil);
}
GL.ColorMask(true, true, true, true);
}

View file

@ -23,7 +23,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
Buffer = new OGLConstBuffer();
FrameBuffer = new OGLFrameBuffer();
Texture = new OGLTexture();
FrameBuffer = new OGLFrameBuffer(Texture as OGLTexture);
Rasterizer = new OGLRasterizer();
@ -31,8 +33,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Pipeline = new OGLPipeline(Buffer as OGLConstBuffer, Rasterizer as OGLRasterizer, Shader as OGLShader);
Texture = new OGLTexture();
ActionsQueue = new ConcurrentQueue<Action>();
}

View file

@ -4,7 +4,7 @@ using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLTexture : IGalTexture
class OGLTexture : IGalTexture
{
private OGLCachedResource<TCE> TextureCache;
@ -95,6 +95,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA);
}
public void CreateFb(long Key, long Size, GalImage Image)
{
if (!TryGetTCE(Key, out TCE Texture))
{
Texture = new TCE();
TextureCache.AddOrUpdate(Key, Texture, Size);
}
Texture.EnsureSetup(Image);
}
public bool TryGetTCE(long Key, out TCE CachedTexture)
{
if (TextureCache.TryGetValue(Key, out CachedTexture))
{
return true;
}
CachedTexture = null;
return false;
}
private static int GetAstcBlockWidth(GalImageFormat Format)
{
switch (Format)

View file

@ -8,6 +8,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
//TODO: Use a variable value here
public const int MaxBpp = 16;
private static int CopyBuffer = 0;
private static int CopyBufferSize = 0;
public GalImage Image { get; private set; }
public int Width { get => Image.Width; }
@ -36,31 +39,37 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void EnsureSetup(GalImage Image)
{
if (this.Width != Image.Width ||
this.Height != Image.Height ||
this.Format != Image.Format ||
if (Width != Image.Width ||
Height != Image.Height ||
Format != Image.Format ||
!Initialized)
{
(PixelInternalFormat InternalFormat, PixelFormat PixelFormat, PixelType PixelType) =
OGLEnumConverter.GetImageFormat(Image.Format);
int CopyBuffer = 0;
bool ChangingFormat = Initialized && this.InternalFormat != InternalFormat;
GL.BindTexture(TextureTarget.Texture2D, Handle);
if (ChangingFormat)
if (Initialized)
{
CopyBuffer = GL.GenBuffer();
if (CopyBuffer == 0)
{
CopyBuffer = GL.GenBuffer();
}
int MaxWidth = Math.Max(Image.Width, Width);
int MaxHeight = Math.Max(Image.Height, Height);
int CurrentSize = MaxWidth * MaxHeight * MaxBpp;
GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer);
int MaxWidth = Math.Max(Image.Width, this.Width);
int MaxHeight = Math.Max(Image.Height, this.Height);
if (CopyBufferSize < CurrentSize)
{
CopyBufferSize = CurrentSize;
GL.BufferData(BufferTarget.PixelPackBuffer, MaxWidth * MaxHeight * MaxBpp, IntPtr.Zero, BufferUsageHint.StaticCopy);
GL.BufferData(BufferTarget.PixelPackBuffer, CurrentSize, IntPtr.Zero, BufferUsageHint.DynamicCopy);
}
GL.GetTexImage(TextureTarget.Texture2D, 0, this.PixelFormat, this.PixelType, IntPtr.Zero);
@ -91,12 +100,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
PixelType,
IntPtr.Zero);
if (ChangingFormat)
if (Initialized)
{
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
GL.DeleteBuffer(CopyBuffer);
}
this.Image = Image;
@ -108,5 +115,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Initialized = true;
}
}
public bool HasColor { get => ImageFormatConverter.HasColor(Format); }
public bool HasDepth { get => ImageFormatConverter.HasDepth(Format); }
public bool HasStencil { get => ImageFormatConverter.HasStencil(Format); }
}
}

View file

@ -102,8 +102,10 @@ namespace Ryujinx.HLE.Gpu.Engines
SetAlphaBlending(State);
SetPrimitiveRestart(State);
SetFrameBuffer(Vmm, 0);
for (int FbIndex = 0; FbIndex < 8; FbIndex++)
{
SetFrameBuffer(Vmm, FbIndex);
}
SetZeta(Vmm);
long[] Keys = UploadShaders(Vmm);
@ -151,6 +153,7 @@ namespace Ryujinx.HLE.Gpu.Engines
int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil);
SetFrameBuffer(Vmm, FbIndex);
SetZeta(Vmm);
Gpu.Renderer.Rasterizer.ClearBuffers(
Flags,
@ -166,7 +169,7 @@ namespace Ryujinx.HLE.Gpu.Engines
int Format = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10);
if (VA == 0/* || Format == 0*/)
if (VA == 0 || Format == 0)
{
Gpu.Renderer.FrameBuffer.UnbindColor(FbIndex);
@ -192,7 +195,13 @@ namespace Ryujinx.HLE.Gpu.Engines
int VpW = (int)(TX + MathF.Abs(SX)) - VpX;
int VpH = (int)(TY + MathF.Abs(SY)) - VpY;
Gpu.Renderer.FrameBuffer.CreateColor(Key, Width, Height, (GalFrameBufferFormat)Format);
GalImageFormat ImageFormat = ImageFormatConverter.ConvertFrameBuffer((GalFrameBufferFormat)Format);
GalImage Image = new GalImage(Width, Height, ImageFormat);
long Size = TextureHelper.GetTextureSize(Image);
Gpu.Renderer.Texture.CreateFb(Key, Size, Image);
Gpu.Renderer.FrameBuffer.BindColor(Key, FbIndex);
Gpu.Renderer.FrameBuffer.SetViewport(VpX, VpY, VpW, VpH);
@ -214,12 +223,17 @@ namespace Ryujinx.HLE.Gpu.Engines
}
long Key = Vmm.GetPhysicalAddress(ZA);
int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz);
int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert);
Gpu.Renderer.FrameBuffer.CreateZeta(Key, Width, Height, (GalZetaFormat)Format);
GalImageFormat ImageFormat = ImageFormatConverter.ConvertZeta((GalZetaFormat)Format);
GalImage Image = new GalImage(Width, Height, ImageFormat);
long Size = TextureHelper.GetTextureSize(Image);
Gpu.Renderer.Texture.CreateFb(Key, Size, Image);
Gpu.Renderer.FrameBuffer.BindZeta(Key);
}
@ -479,15 +493,15 @@ namespace Ryujinx.HLE.Gpu.Engines
}
else
{
GalImage NewTexture = TextureFactory.MakeTexture(Vmm, TicPosition);
GalImage NewImage = TextureFactory.MakeTexture(Vmm, TicPosition);
long Size = (uint)TextureHelper.GetTextureSize(NewTexture);
long Size = (uint)TextureHelper.GetTextureSize(NewImage);
bool HasCachedTexture = false;
if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalImage Texture))
if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalImage Image))
{
if (NewTexture.Equals(Texture) && !QueryKeyUpload(Vmm, Key, Size, NvGpuBufferType.Texture))
if (NewImage.Equals(Image) && !QueryKeyUpload(Vmm, Key, Size, NvGpuBufferType.Texture))
{
Gpu.Renderer.Texture.Bind(Key, TexIndex);
@ -499,7 +513,7 @@ namespace Ryujinx.HLE.Gpu.Engines
{
byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition);
Gpu.Renderer.Texture.Create(Key, Data, NewTexture);
Gpu.Renderer.Texture.Create(Key, Data, NewImage);
}
Gpu.Renderer.Texture.Bind(Key, TexIndex);