diff options
Diffstat (limited to 'src/Ryujinx.Graphics.OpenGL/Framebuffer.cs')
-rw-r--r-- | src/Ryujinx.Graphics.OpenGL/Framebuffer.cs | 247 |
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; + } + } + } +} |