Files
the-beast-roofer-edition/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs
T
rooferdev 6634d3ef87 The Beast Roofer Edition v1.2.4
Removes a bug introduced in v1.2.3: the DLSS clip-space jitter block was
emitted into every vertex shader and read the Position output before
writing it. Reading an unwritten output is undefined, so vertex positions
could come out as garbage - whole 3D passes landing nowhere (the green
screen reported on GYLT, Ghostbusters, Blair Witch and others) and the
black block artifacts in Xenoblade Chronicles 2's motion-blur pass, which
had been mistaken for a long-standing emulation defect and worked around
by disabling the effect. With jitter off - how this fork ships - the
vertex epilogue is now identical to the base emulator again, and the
motion blur no longer needs to be disabled. CodeGenVersion bumped so
existing shader caches are rebuilt.

Also adds a DLSS sharpening slider (0-100, off by default) and an
experimental advanced motion stack checkbox (off by default).
2026-08-03 13:44:30 -04:00

242 lines
6.7 KiB
C#

using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Image;
using System;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.OpenGL
{
class Framebuffer : IDisposable
{
public int Handle { get; private set; }
private int _clearFbHandle;
private bool _clearFbInitialized;
private FramebufferAttachment _lastDsAttachment;
private readonly TextureView[] _colors;
private TextureView _depthStencil;
private int _colorsCount;
private bool _dualSourceBlend;
public Framebuffer()
{
Handle = GL.GenFramebuffer();
_clearFbHandle = GL.GenFramebuffer();
_colors = new TextureView[8];
}
public int Bind()
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle);
return Handle;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// [GLDUMP] (journal 216) zero-alloc accessors for the GL-side chain dump probe.
public TextureView MvppGetColor(int index)
{
return (uint)index < (uint)_colors.Length ? _colors[index] : null;
}
public void AttachColor(int index, TextureView color)
{
if (_colors[index] == color)
{
return;
}
FramebufferAttachment attachment = FramebufferAttachment.ColorAttachment0 + index;
GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.Handle ?? 0, 0);
_colors[index] = color;
}
public void AttachDepthStencil(TextureView depthStencil)
{
// Detach the last depth/stencil buffer if there is any.
if (_lastDsAttachment != 0)
{
GL.FramebufferTexture(FramebufferTarget.Framebuffer, _lastDsAttachment, 0, 0);
}
if (depthStencil != null)
{
FramebufferAttachment attachment = GetAttachment(depthStencil.Format);
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
attachment,
depthStencil.Handle,
0);
_lastDsAttachment = attachment;
}
else
{
_lastDsAttachment = 0;
}
_depthStencil = depthStencil;
}
public void SetDualSourceBlend(bool enable)
{
bool oldEnable = _dualSourceBlend;
_dualSourceBlend = enable;
// When dual source blend is used,
// we can only have one draw buffer.
if (enable)
{
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
}
else if (oldEnable)
{
SetDrawBuffersImpl(_colorsCount);
}
}
public void SetDrawBuffers(int colorsCount)
{
if (_colorsCount != colorsCount && !_dualSourceBlend)
{
SetDrawBuffersImpl(colorsCount);
}
_colorsCount = colorsCount;
}
private static void SetDrawBuffersImpl(int colorsCount)
{
DrawBuffersEnum[] drawBuffers = new DrawBuffersEnum[colorsCount];
for (int index = 0; index < colorsCount; index++)
{
drawBuffers[index] = DrawBuffersEnum.ColorAttachment0 + index;
}
GL.DrawBuffers(colorsCount, drawBuffers);
}
private static FramebufferAttachment GetAttachment(Format format)
{
if (FormatTable.IsPackedDepthStencil(format))
{
return FramebufferAttachment.DepthStencilAttachment;
}
else if (FormatTable.IsDepthOnly(format))
{
return FramebufferAttachment.DepthAttachment;
}
else
{
return FramebufferAttachment.StencilAttachment;
}
}
public int GetColorLayerCount(int index)
{
return _colors[index]?.Info.GetDepthOrLayers() ?? 0;
}
public int GetDepthStencilLayerCount()
{
return _depthStencil?.Info.GetDepthOrLayers() ?? 0;
}
public void AttachColorLayerForClear(int index, int layer)
{
TextureView color = _colors[index];
if (!IsLayered(color))
{
return;
}
BindClearFb();
GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, color.Handle, 0, layer);
}
public void DetachColorLayerForClear(int index)
{
TextureView color = _colors[index];
if (!IsLayered(color))
{
return;
}
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, 0, 0);
Bind();
}
public void AttachDepthStencilLayerForClear(int layer)
{
TextureView depthStencil = _depthStencil;
if (!IsLayered(depthStencil))
{
return;
}
BindClearFb();
GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), depthStencil.Handle, 0, layer);
}
public void DetachDepthStencilLayerForClear()
{
TextureView depthStencil = _depthStencil;
if (!IsLayered(depthStencil))
{
return;
}
GL.FramebufferTexture(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), 0, 0);
Bind();
}
private void BindClearFb()
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, _clearFbHandle);
if (!_clearFbInitialized)
{
SetDrawBuffersImpl(Constants.MaxRenderTargets);
_clearFbInitialized = true;
}
}
private static bool IsLayered(TextureView view)
{
return view != null &&
view.Target != Target.Texture1D &&
view.Target != Target.Texture2D &&
view.Target != Target.Texture2DMultisample &&
view.Target != Target.TextureBuffer;
}
public void Dispose()
{
if (Handle != 0)
{
GL.DeleteFramebuffer(Handle);
Handle = 0;
}
if (_clearFbHandle != 0)
{
GL.DeleteFramebuffer(_clearFbHandle);
_clearFbHandle = 0;
}
}
}
}