aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.OpenGL/Framebuffer.cs')
-rw-r--r--src/Ryujinx.Graphics.OpenGL/Framebuffer.cs247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs
new file mode 100644
index 00000000..b180b857
--- /dev/null
+++ b/src/Ryujinx.Graphics.OpenGL/Framebuffer.cs
@@ -0,0 +1,247 @@
+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)]
+ 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 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 (IsPackedDepthStencilFormat(format))
+ {
+ return FramebufferAttachment.DepthStencilAttachment;
+ }
+ else if (IsDepthOnlyFormat(format))
+ {
+ return FramebufferAttachment.DepthAttachment;
+ }
+ else
+ {
+ return FramebufferAttachment.StencilAttachment;
+ }
+ }
+
+ private static bool IsPackedDepthStencilFormat(Format format)
+ {
+ return format == Format.D24UnormS8Uint ||
+ format == Format.D32FloatS8Uint ||
+ format == Format.S8UintD24Unorm;
+ }
+
+ private static bool IsDepthOnlyFormat(Format format)
+ {
+ return format == Format.D16Unorm || format == Format.D32Float;
+ }
+
+ 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;
+ }
+ }
+ }
+}