Fix multiple rendertargets (#427)

* 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
This commit is contained in:
ReinUsesLisp 2018-09-25 19:55:30 -03:00 committed by Ac_K
parent 7de7b559ad
commit 2562ca6c3f
8 changed files with 185 additions and 155 deletions

View file

@ -2,15 +2,17 @@ namespace Ryujinx.Graphics.Gal
{ {
public interface IGalRenderTarget public interface IGalRenderTarget
{ {
void BindColor(long Key, int Attachment, GalImage Image); void Bind();
void BindColor(long Key, int Attachment);
void UnbindColor(int Attachment); void UnbindColor(int Attachment);
void BindZeta(long Key, GalImage Image); void BindZeta(long Key);
void UnbindZeta(); void UnbindZeta();
void Set(long Key); void Present(long Key);
void SetMap(int[] Map); void SetMap(int[] Map);
@ -18,7 +20,7 @@ namespace Ryujinx.Graphics.Gal
void SetWindowSize(int Width, int Height); void SetWindowSize(int Width, int Height);
void SetViewport(int X, int Y, int Width, int Height); void SetViewport(int Attachment, int X, int Y, int Width, int Height);
void Render(); void Render();

View file

@ -225,7 +225,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp; case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
} }
if (OGLExtension.HasTextureMirrorClamp()) if (OGLExtension.TextureMirrorClamp)
{ {
switch (Wrap) switch (Wrap)
{ {

View file

@ -1,40 +1,17 @@
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
static class OGLExtension static class OGLExtension
{ {
private static bool Initialized = false; private static Lazy<bool> s_EnhancedLayouts = new Lazy<bool>(() => HasExtension("GL_ARB_enhanced_layouts"));
private static Lazy<bool> s_TextureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> s_ViewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
private static bool EnhancedLayouts; public static bool EnhancedLayouts => s_EnhancedLayouts.Value;
public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value;
private static bool TextureMirrorClamp; public static bool ViewportArray => s_ViewportArray.Value;
public static bool HasEnhancedLayouts()
{
EnsureInitialized();
return EnhancedLayouts;
}
public static bool HasTextureMirrorClamp()
{
EnsureInitialized();
return TextureMirrorClamp;
}
private static void EnsureInitialized()
{
if (Initialized)
{
return;
}
EnhancedLayouts = HasExtension("GL_ARB_enhanced_layouts");
TextureMirrorClamp = HasExtension("GL_EXT_texture_mirror_clamp");
}
private static bool HasExtension(string Name) private static bool HasExtension(string Name)
{ {

View file

@ -22,16 +22,52 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
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 NativeWidth = 1280;
private const int NativeHeight = 720; private const int NativeHeight = 720;
private const int RenderTargetsCount = 8;
private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
private OGLTexture Texture; private OGLTexture Texture;
private ImageHandler ReadTex; private ImageHandler ReadTex;
private Rect Viewport; private float[] Viewports;
private Rect Window; private Rect Window;
private bool FlipX; private bool FlipX;
@ -50,138 +86,164 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private int SrcFb; private int SrcFb;
private int DstFb; private int DstFb;
//Holds current attachments, used to avoid unnecesary calls to OpenGL private FrameBufferAttachments Attachments;
private int[] ColorAttachments; private FrameBufferAttachments OldAttachments;
private int DepthAttachment;
private int StencilAttachment;
private int CopyPBO; private int CopyPBO;
public OGLRenderTarget(OGLTexture Texture) public OGLRenderTarget(OGLTexture Texture)
{ {
ColorAttachments = new int[8]; Attachments = new FrameBufferAttachments();
OldAttachments = new FrameBufferAttachments();
Viewports = new float[RenderTargetsCount * 4];
this.Texture = Texture; this.Texture = Texture;
} }
public void BindColor(long Key, int Attachment, GalImage Image) public void Bind()
{ {
if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage)) if (DummyFrameBuffer == 0)
{ {
EnsureFrameBuffer(); DummyFrameBuffer = GL.GenFramebuffer();
Attach(ref ColorAttachments[Attachment], CachedImage.Handle, FramebufferAttachment.ColorAttachment0 + Attachment);
} }
else
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
ImageHandler CachedImage;
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
{ {
UnbindColor(Attachment); if (Attachments.Colors[Attachment] == OldAttachments.Colors[Attachment])
}
}
public void UnbindColor(int Attachment)
{
EnsureFrameBuffer();
Attach(ref ColorAttachments[Attachment], 0, FramebufferAttachment.ColorAttachment0 + Attachment);
}
public void BindZeta(long Key, GalImage Image)
{
if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
{
EnsureFrameBuffer();
if (CachedImage.HasDepth && CachedImage.HasStencil)
{ {
if (DepthAttachment != CachedImage.Handle || continue;
StencilAttachment != CachedImage.Handle) }
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( GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer, FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment, FramebufferAttachment.DepthStencilAttachment,
CachedImage.Handle, CachedImage.Handle,
0); 0);
DepthAttachment = CachedImage.Handle;
StencilAttachment = CachedImage.Handle;
} }
} else if (CachedImage.HasDepth)
else if (CachedImage.HasDepth) {
{ GL.FramebufferTexture(
Attach(ref DepthAttachment, CachedImage.Handle, FramebufferAttachment.DepthAttachment); FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthAttachment,
CachedImage.Handle,
0);
Attach(ref StencilAttachment, 0, FramebufferAttachment.StencilAttachment); GL.FramebufferTexture(
} FramebufferTarget.DrawFramebuffer,
else if (CachedImage.HasStencil) FramebufferAttachment.StencilAttachment,
{ 0,
Attach(ref DepthAttachment, 0, FramebufferAttachment.DepthAttachment); 0);
}
Attach(ref StencilAttachment, CachedImage.Handle, FramebufferAttachment.StencilAttachment); else
{
throw new NotImplementedException();
}
} }
else else
{ {
throw new InvalidOperationException(); GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
} }
} }
if (OGLExtension.ViewportArray)
{
GL.ViewportArray(0, 8, Viewports);
}
else else
{ {
UnbindZeta(); GL.Viewport(
(int)Viewports[0],
(int)Viewports[1],
(int)Viewports[2],
(int)Viewports[3]);
} }
}
private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment) if (Attachments.MapCount > 1)
{
if (OldHandle != NewHandle)
{ {
GL.FramebufferTexture( GL.DrawBuffers(Attachments.MapCount, Attachments.Map);
FramebufferTarget.DrawFramebuffer,
FbAttachment,
NewHandle,
0);
OldHandle = NewHandle;
} }
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() public void UnbindZeta()
{ {
EnsureFrameBuffer(); Attachments.Zeta = 0;
if (DepthAttachment != 0 || StencilAttachment != 0)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
DepthAttachment = 0;
StencilAttachment = 0;
}
} }
public void Set(long Key) public void Present(long Key)
{ {
Texture.TryGetImageHandler(Key, out ReadTex); Texture.TryGetImageHandler(Key, out ReadTex);
} }
public void SetMap(int[] Map) public void SetMap(int[] Map)
{ {
if (Map != null && Map.Length > 0) if (Map != null)
{ {
DrawBuffersEnum[] Mode = new DrawBuffersEnum[Map.Length]; Attachments.MapCount = Map.Length;
for (int i = 0; i < Map.Length; i++) for (int Attachment = 0; Attachment < Attachments.MapCount; Attachment++)
{ {
Mode[i] = DrawBuffersEnum.ColorAttachment0 + Map[i]; Attachments.Map[Attachment] = DrawBuffersEnum.ColorAttachment0 + Map[Attachment];
} }
GL.DrawBuffers(Mode.Length, Mode);
} }
else else
{ {
GL.DrawBuffer(DrawBufferMode.ColorAttachment0); Attachments.MapCount = 0;
} }
} }
@ -201,20 +263,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Window = new Rect(0, 0, Width, Height); Window = new Rect(0, 0, Width, Height);
} }
public void SetViewport(int X, int Y, int Width, int Height) public void SetViewport(int Attachment, int X, int Y, int Width, int Height)
{ {
Viewport = new Rect(X, Y, Width, Height); int Offset = Attachment * 4;
SetViewport(Viewport); Viewports[Offset + 0] = X;
} Viewports[Offset + 1] = Y;
Viewports[Offset + 2] = Width;
private void SetViewport(Rect Viewport) Viewports[Offset + 3] = Height;
{
GL.Viewport(
Viewport.X,
Viewport.Y,
Viewport.Width,
Viewport.Height);
} }
public void Render() public void Render()
@ -276,7 +332,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0); GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0);
GL.ReadBuffer(ReadBufferMode.ColorAttachment0); GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
GL.Clear(ClearBufferMask.ColorBufferBit); GL.Clear(ClearBufferMask.ColorBufferBit);
@ -285,8 +340,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
DstX0, DstY0, DstX1, DstY1, DstX0, DstY0, DstX1, DstY1,
ClearBufferMask.ColorBufferBit, ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear); BlitFramebufferFilter.Linear);
EnsureFrameBuffer();
} }
public void Copy( public void Copy(
@ -343,8 +396,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Clear(Mask); GL.Clear(Mask);
GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter); GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter);
EnsureFrameBuffer();
} }
} }
@ -419,15 +470,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL
(CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) | (CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) |
(CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0); (CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0);
} }
private void EnsureFrameBuffer()
{
if (DummyFrameBuffer == 0)
{
DummyFrameBuffer = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
}
} }
} }

View file

@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
//Enhanced layouts are required for Geometry shaders //Enhanced layouts are required for Geometry shaders
//skip this stage if current driver has no ARB_enhanced_layouts //skip this stage if current driver has no ARB_enhanced_layouts
if (!OGLExtension.HasEnhancedLayouts()) if (!OGLExtension.EnhancedLayouts)
{ {
return; return;
} }

View file

@ -46,7 +46,7 @@ namespace Ryujinx.Graphics
Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage);
} }
Gpu.Renderer.RenderTarget.BindColor(Position, Attachment, NewImage); Gpu.Renderer.RenderTarget.BindColor(Position, Attachment);
} }
public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage) public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage)
@ -60,7 +60,7 @@ namespace Ryujinx.Graphics
Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage);
} }
Gpu.Renderer.RenderTarget.BindZeta(Position, NewImage); Gpu.Renderer.RenderTarget.BindZeta(Position);
} }
public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage, int TexIndex = -1) public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage, int TexIndex = -1)

View file

@ -100,7 +100,10 @@ namespace Ryujinx.Graphics
SetAlphaBlending(State); SetAlphaBlending(State);
SetPrimitiveRestart(State); SetPrimitiveRestart(State);
SetFrameBuffer(Vmm, 0); for (int FbIndex = 0; FbIndex < 8; FbIndex++)
{
SetFrameBuffer(Vmm, FbIndex);
}
SetZeta(Vmm); SetZeta(Vmm);
@ -154,6 +157,10 @@ namespace Ryujinx.Graphics
SetZeta(Vmm); SetZeta(Vmm);
SetRenderTargets();
Gpu.Renderer.RenderTarget.Bind();
Gpu.Renderer.Rasterizer.ClearBuffers( Gpu.Renderer.Rasterizer.ClearBuffers(
Flags, Flags,
FbIndex, FbIndex,
@ -204,7 +211,7 @@ namespace Ryujinx.Graphics
Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image);
Gpu.Renderer.RenderTarget.SetViewport(VpX, VpY, VpW, VpH); Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH);
} }
private void SetFrameBuffer(GalPipelineState State) private void SetFrameBuffer(GalPipelineState State)
@ -426,14 +433,15 @@ namespace Ryujinx.Graphics
private void SetRenderTargets() private void SetRenderTargets()
{ {
bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); //Commercial games do not seem to
//bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData);
if (SeparateFragData) uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl));
uint Count = Control & 0xf;
if (Count > 0)
{ {
uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl));
uint Count = Control & 0xf;
int[] Map = new int[Count]; int[] Map = new int[Count];
for (int i = 0; i < Count; i++) for (int i = 0; i < Count; i++)
@ -702,6 +710,8 @@ namespace Ryujinx.Graphics
Gpu.Renderer.Pipeline.Bind(State); Gpu.Renderer.Pipeline.Bind(State);
Gpu.Renderer.RenderTarget.Bind();
if (IndexCount != 0) if (IndexCount != 0)
{ {
int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);

View file

@ -328,7 +328,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
Context.Device.Gpu.ResourceManager.SendTexture(Vmm, FbAddr, Image); Context.Device.Gpu.ResourceManager.SendTexture(Vmm, FbAddr, Image);
Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom); Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom);
Renderer.RenderTarget.Set(FbAddr); Renderer.RenderTarget.Present(FbAddr);
ReleaseBuffer(Slot); ReleaseBuffer(Slot);
}); });