From 77840b71dbe12103eec916200936a9a7cb58ff6a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 30 Jul 2022 00:12:51 +0900 Subject: [PATCH 1/8] Add IFrameBuffer --- .../Graphics/Rendering/IFrameBuffer.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 osu.Framework/Graphics/Rendering/IFrameBuffer.cs diff --git a/osu.Framework/Graphics/Rendering/IFrameBuffer.cs b/osu.Framework/Graphics/Rendering/IFrameBuffer.cs new file mode 100644 index 0000000000..fabbc62816 --- /dev/null +++ b/osu.Framework/Graphics/Rendering/IFrameBuffer.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics.Textures; +using osuTK; + +namespace osu.Framework.Graphics.Rendering +{ + public interface IFrameBuffer : IDisposable + { + /// + /// The framebuffer's backing texture. + /// + Texture Texture { get; } + + /// + /// The framebuffer's texture size. + /// + Vector2 Size { get; set; } + + /// + /// Binds the framebuffer. + /// Does not clear the buffer or reset the viewport/ortho. + /// + void Bind(); + + /// + /// Unbinds the framebuffer. + /// + void Unbind(); + } +} From 426d739dc3cb5fdc57824c47ca34ad5a87d8097d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 30 Jul 2022 00:13:03 +0900 Subject: [PATCH 2/8] Make FrameBuffer internal --- osu.Framework/Graphics/BufferedDrawNode.cs | 9 +++-- .../Graphics/BufferedDrawNodeSharedData.cs | 35 ++++++++++++------- .../Containers/BufferedContainer_DrawNode.cs | 5 ++- osu.Framework/Graphics/DrawNode.cs | 4 +-- .../Graphics/OpenGL/Buffers/FrameBuffer.cs | 24 +++++++------ .../Graphics/OpenGL/OpenGLRenderer.cs | 4 +++ osu.Framework/Graphics/Rendering/IRenderer.cs | 9 +++++ 7 files changed, 58 insertions(+), 32 deletions(-) diff --git a/osu.Framework/Graphics/BufferedDrawNode.cs b/osu.Framework/Graphics/BufferedDrawNode.cs index ba20d9e83d..eaf6f79426 100644 --- a/osu.Framework/Graphics/BufferedDrawNode.cs +++ b/osu.Framework/Graphics/BufferedDrawNode.cs @@ -85,6 +85,8 @@ public override void ApplyState() public sealed override void Draw(IRenderer renderer) { + SharedData.Initialise(renderer); + if (RequiresRedraw) { FrameStatistics.Increment(StatisticsCounterType.FBORedraw); @@ -143,14 +145,14 @@ protected virtual void DrawContents(IRenderer renderer) /// /// The to bind. /// A token that must be disposed upon finishing use of . - protected IDisposable BindFrameBuffer(FrameBuffer frameBuffer) + protected IDisposable BindFrameBuffer(IFrameBuffer frameBuffer) { // This setter will also take care of allocating a texture of appropriate size within the frame buffer. frameBuffer.Size = frameBufferSize; frameBuffer.Bind(); - return new ValueInvokeOnDisposal(frameBuffer, b => b.Unbind()); + return new ValueInvokeOnDisposal(frameBuffer, b => b.Unbind()); } private IDisposable establishFrameBufferViewport() @@ -158,7 +160,8 @@ private IDisposable establishFrameBufferViewport() // Disable masking for generating the frame buffer since masking will be re-applied // when actually drawing later on anyways. This allows more information to be captured // in the frame buffer and helps with cached buffers being re-used. - RectangleI screenSpaceMaskingRect = new RectangleI((int)Math.Floor(screenSpaceDrawRectangle.X), (int)Math.Floor(screenSpaceDrawRectangle.Y), (int)frameBufferSize.X + 1, (int)frameBufferSize.Y + 1); + RectangleI screenSpaceMaskingRect = new RectangleI((int)Math.Floor(screenSpaceDrawRectangle.X), (int)Math.Floor(screenSpaceDrawRectangle.Y), (int)frameBufferSize.X + 1, + (int)frameBufferSize.Y + 1); GLWrapper.PushMaskingInfo(new MaskingInfo { diff --git a/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs b/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs index 9256ce9085..e7cca39522 100644 --- a/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs +++ b/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs @@ -6,6 +6,7 @@ using System; using osu.Framework.Graphics.OpenGL; using osu.Framework.Graphics.OpenGL.Buffers; +using osu.Framework.Graphics.Rendering; using osuTK.Graphics.ES30; namespace osu.Framework.Graphics @@ -27,7 +28,7 @@ public class BufferedDrawNodeSharedData : IDisposable /// /// The which contains the original version of the rendered . /// - public FrameBuffer MainBuffer { get; } + public IFrameBuffer MainBuffer { get; private set; } /// /// Whether the frame buffer position should be snapped to the nearest pixel when blitting. @@ -43,7 +44,9 @@ public class BufferedDrawNodeSharedData : IDisposable /// /// A set of s which are used in a ping-pong manner to render effects to. /// - private readonly FrameBuffer[] effectBuffers; + private readonly IFrameBuffer[] effectBuffers; + + private readonly RenderbufferInternalFormat[] formats; /// /// Creates a new with no effect buffers. @@ -67,16 +70,23 @@ public BufferedDrawNodeSharedData(int effectBufferCount, RenderbufferInternalFor if (effectBufferCount < 0) throw new ArgumentOutOfRangeException(nameof(effectBufferCount), "Must be positive."); + this.formats = formats; PixelSnapping = pixelSnapping; - All filterMode = pixelSnapping ? All.Nearest : All.Linear; - ClipToRootNode = clipToRootNode; - MainBuffer = new FrameBuffer(formats, filterMode); - effectBuffers = new FrameBuffer[effectBufferCount]; + effectBuffers = new IFrameBuffer[effectBufferCount]; + } + + public void Initialise(IRenderer renderer) + { + if (MainBuffer != null) + return; + + All filterMode = PixelSnapping ? All.Nearest : All.Linear; - for (int i = 0; i < effectBufferCount; i++) - effectBuffers[i] = new FrameBuffer(formats, filterMode); + MainBuffer = renderer.CreateFrameBuffer(formats, filterMode); + for (int i = 0; i < effectBuffers.Length; i++) + effectBuffers[i] = renderer.CreateFrameBuffer(formats, filterMode); } private int currentEffectBuffer = -1; @@ -84,13 +94,13 @@ public BufferedDrawNodeSharedData(int effectBufferCount, RenderbufferInternalFor /// /// The which contains the most up-to-date drawn effect. /// - public FrameBuffer CurrentEffectBuffer => currentEffectBuffer == -1 ? MainBuffer : effectBuffers[currentEffectBuffer]; + public IFrameBuffer CurrentEffectBuffer => currentEffectBuffer == -1 ? MainBuffer : effectBuffers[currentEffectBuffer]; /// /// Retrieves the next which effects can be rendered to. /// /// If there are no available effect buffers. - public FrameBuffer GetNextEffectBuffer() + public IFrameBuffer GetNextEffectBuffer() { if (effectBuffers.Length == 0) throw new InvalidOperationException($"The {nameof(BufferedDrawNode)} requested an effect buffer, but none were available."); @@ -114,10 +124,9 @@ public void Dispose() protected virtual void Dispose(bool isDisposing) { - MainBuffer.Dispose(); - + MainBuffer?.Dispose(); for (int i = 0; i < effectBuffers.Length; i++) - effectBuffers[i].Dispose(); + effectBuffers[i]?.Dispose(); } } } diff --git a/osu.Framework/Graphics/Containers/BufferedContainer_DrawNode.cs b/osu.Framework/Graphics/Containers/BufferedContainer_DrawNode.cs index 3fd2d9249a..36091df5b2 100644 --- a/osu.Framework/Graphics/Containers/BufferedContainer_DrawNode.cs +++ b/osu.Framework/Graphics/Containers/BufferedContainer_DrawNode.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using osu.Framework.Graphics.OpenGL; -using osu.Framework.Graphics.OpenGL.Buffers; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Primitives; @@ -97,8 +96,8 @@ protected override void DrawContents(IRenderer renderer) private void drawBlurredFrameBuffer(int kernelRadius, float sigma, float blurRotation) { - FrameBuffer current = SharedData.CurrentEffectBuffer; - FrameBuffer target = SharedData.GetNextEffectBuffer(); + IFrameBuffer current = SharedData.CurrentEffectBuffer; + IFrameBuffer target = SharedData.GetNextEffectBuffer(); GLWrapper.SetBlend(BlendingParameters.None); diff --git a/osu.Framework/Graphics/DrawNode.cs b/osu.Framework/Graphics/DrawNode.cs index 0b880a5d11..28d1b60f53 100644 --- a/osu.Framework/Graphics/DrawNode.cs +++ b/osu.Framework/Graphics/DrawNode.cs @@ -268,13 +268,13 @@ protected void DrawClipped(ref T polygon, TextureGL texture, ColourInfo drawC /// An action that adds vertices to a . /// The percentage amount that the frame buffer area should be inflated. /// The range over which the edges of the frame buffer should be blended. - protected void DrawFrameBuffer(FrameBuffer frameBuffer, Quad vertexQuad, ColourInfo drawColour, Action vertexAction = null, + protected void DrawFrameBuffer(IFrameBuffer frameBuffer, Quad vertexQuad, ColourInfo drawColour, Action vertexAction = null, Vector2? inflationPercentage = null, Vector2? blendRangeOverride = null) { // The strange Y coordinate and Height are a result of OpenGL coordinate systems having Y grow upwards and not downwards. RectangleF textureRect = new RectangleF(0, frameBuffer.Texture.Height, frameBuffer.Texture.Width, -frameBuffer.Texture.Height); - if (frameBuffer.Texture.Bind()) + if (frameBuffer.Texture.TextureGL.Bind()) DrawQuad(frameBuffer.Texture, vertexQuad, drawColour, textureRect, vertexAction, inflationPercentage, blendRangeOverride); } diff --git a/osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs b/osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs index c2be6b8c0a..ebb09f14f1 100644 --- a/osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs +++ b/osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs @@ -6,21 +6,23 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Textures; using osuTK; using osuTK.Graphics.ES30; namespace osu.Framework.Graphics.OpenGL.Buffers { - public class FrameBuffer : IDisposable + internal class FrameBuffer : IFrameBuffer { - private int frameBuffer = -1; + public Texture Texture { get; private set; } - public TextureGL Texture { get; private set; } + private int frameBuffer = -1; private readonly List attachedRenderBuffers = new List(); private bool isInitialised; + private TextureGL textureGL; private readonly All filteringMode; private readonly RenderbufferInternalFormat[] renderBufferFormats; @@ -48,11 +50,11 @@ public Vector2 Size if (isInitialised) { - Texture.Width = (int)Math.Ceiling(size.X); - Texture.Height = (int)Math.Ceiling(size.Y); + textureGL.Width = (int)Math.Ceiling(size.X); + textureGL.Height = (int)Math.Ceiling(size.Y); - Texture.SetData(new TextureUpload()); - Texture.Upload(); + textureGL.SetData(new TextureUpload()); + textureGL.Upload(); } } } @@ -60,11 +62,11 @@ public Vector2 Size private void initialise() { frameBuffer = GL.GenFramebuffer(); - Texture = new FrameBufferTexture(Size, filteringMode); + Texture = new Texture(textureGL = new FrameBufferTexture(Size, filteringMode)); GLWrapper.BindFrameBuffer(frameBuffer); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget2d.Texture2D, Texture.TextureId, 0); + GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget2d.Texture2D, textureGL.TextureId, 0); GLWrapper.BindTexture(null); if (renderBufferFormats != null) @@ -131,8 +133,8 @@ protected virtual void Dispose(bool disposing) if (isInitialised) { - Texture?.Dispose(); - Texture = null; + textureGL?.Dispose(); + textureGL = null; GLWrapper.DeleteFrameBuffer(frameBuffer); diff --git a/osu.Framework/Graphics/OpenGL/OpenGLRenderer.cs b/osu.Framework/Graphics/OpenGL/OpenGLRenderer.cs index e212c876d9..662e180791 100644 --- a/osu.Framework/Graphics/OpenGL/OpenGLRenderer.cs +++ b/osu.Framework/Graphics/OpenGL/OpenGLRenderer.cs @@ -1,11 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.Rendering; +using osuTK.Graphics.ES30; namespace osu.Framework.Graphics.OpenGL { public class OpenGLRenderer : IRenderer { + public IFrameBuffer CreateFrameBuffer(RenderbufferInternalFormat[]? renderBufferFormats = null, All filteringMode = All.Linear) + => new FrameBuffer(renderBufferFormats, filteringMode); } } diff --git a/osu.Framework/Graphics/Rendering/IRenderer.cs b/osu.Framework/Graphics/Rendering/IRenderer.cs index bb69e01b24..003490b3e3 100644 --- a/osu.Framework/Graphics/Rendering/IRenderer.cs +++ b/osu.Framework/Graphics/Rendering/IRenderer.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osuTK.Graphics.ES30; + namespace osu.Framework.Graphics.Rendering { /// @@ -8,5 +10,12 @@ namespace osu.Framework.Graphics.Rendering /// public interface IRenderer { + /// + /// Creates a new . + /// + /// Any render buffer formats. + /// The texture filtering mode. + /// The . + IFrameBuffer CreateFrameBuffer(RenderbufferInternalFormat[]? renderBufferFormats = null, All filteringMode = All.Linear); } } From be553d4e24a75d60f5d65d4bf128ef8178c2e778 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 30 Jul 2022 00:29:14 +0900 Subject: [PATCH 3/8] Avoid ES30 references when creating frame buffers --- .../Graphics/BufferedDrawNodeSharedData.cs | 10 ++--- .../Graphics/Containers/BufferedContainer.cs | 6 +-- .../Containers/BufferedContainer_DrawNode.cs | 3 +- osu.Framework/Graphics/Lines/Path.cs | 4 +- .../Graphics/OpenGL/OpenGLRenderer.cs | 43 ++++++++++++++++++- osu.Framework/Graphics/Rendering/IRenderer.cs | 4 +- .../Graphics/Rendering/RenderBufferFormat.cs | 13 ++++++ 7 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 osu.Framework/Graphics/Rendering/RenderBufferFormat.cs diff --git a/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs b/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs index e7cca39522..2622ddbb07 100644 --- a/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs +++ b/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.OpenGL; using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.Rendering; -using osuTK.Graphics.ES30; +using osu.Framework.Graphics.Textures; namespace osu.Framework.Graphics { @@ -46,12 +46,12 @@ public class BufferedDrawNodeSharedData : IDisposable /// private readonly IFrameBuffer[] effectBuffers; - private readonly RenderbufferInternalFormat[] formats; + private readonly RenderBufferFormat[] formats; /// /// Creates a new with no effect buffers. /// - public BufferedDrawNodeSharedData(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false, bool clipToRootNode = false) + public BufferedDrawNodeSharedData(RenderBufferFormat[] formats = null, bool pixelSnapping = false, bool clipToRootNode = false) : this(0, formats, pixelSnapping, clipToRootNode) { } @@ -65,7 +65,7 @@ public BufferedDrawNodeSharedData(RenderbufferInternalFormat[] formats = null, b /// This amounts to setting the texture filtering mode to "nearest". /// Whether the frame buffer should be clipped to be contained in the root node.. /// If is less than 0. - public BufferedDrawNodeSharedData(int effectBufferCount, RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false, bool clipToRootNode = false) + public BufferedDrawNodeSharedData(int effectBufferCount, RenderBufferFormat[] formats = null, bool pixelSnapping = false, bool clipToRootNode = false) { if (effectBufferCount < 0) throw new ArgumentOutOfRangeException(nameof(effectBufferCount), "Must be positive."); @@ -82,7 +82,7 @@ public void Initialise(IRenderer renderer) if (MainBuffer != null) return; - All filterMode = PixelSnapping ? All.Nearest : All.Linear; + TextureFilteringMode filterMode = PixelSnapping ? TextureFilteringMode.Nearest : TextureFilteringMode.Linear; MainBuffer = renderer.CreateFrameBuffer(formats, filterMode); for (int i = 0; i < effectBuffers.Length; i++) diff --git a/osu.Framework/Graphics/Containers/BufferedContainer.cs b/osu.Framework/Graphics/Containers/BufferedContainer.cs index 36d74fef36..44430fc60a 100644 --- a/osu.Framework/Graphics/Containers/BufferedContainer.cs +++ b/osu.Framework/Graphics/Containers/BufferedContainer.cs @@ -5,10 +5,10 @@ using osuTK; using osuTK.Graphics; -using osuTK.Graphics.ES30; using osu.Framework.Allocation; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Shaders; using osu.Framework.Utils; using osu.Framework.Graphics.Sprites; @@ -28,7 +28,7 @@ namespace osu.Framework.Graphics.Containers public class BufferedContainer : BufferedContainer { /// - public BufferedContainer(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false, bool cachedFrameBuffer = false) + public BufferedContainer(RenderBufferFormat[] formats = null, bool pixelSnapping = false, bool cachedFrameBuffer = false) : base(formats, pixelSnapping, cachedFrameBuffer) { } @@ -255,7 +255,7 @@ public bool RedrawOnScale /// or the size of the container (i.e. framebuffer) changes. /// When disabled, drawing will be clipped to the game window bounds. Enabling can allow drawing larger than (or outside) the game window bounds. /// - public BufferedContainer(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false, bool cachedFrameBuffer = false) + public BufferedContainer(RenderBufferFormat[] formats = null, bool pixelSnapping = false, bool cachedFrameBuffer = false) { UsingCachedFrameBuffer = cachedFrameBuffer; diff --git a/osu.Framework/Graphics/Containers/BufferedContainer_DrawNode.cs b/osu.Framework/Graphics/Containers/BufferedContainer_DrawNode.cs index 36091df5b2..dc3e9a0896 100644 --- a/osu.Framework/Graphics/Containers/BufferedContainer_DrawNode.cs +++ b/osu.Framework/Graphics/Containers/BufferedContainer_DrawNode.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Rendering; using osu.Framework.Utils; -using osuTK.Graphics.ES30; namespace osu.Framework.Graphics.Containers { @@ -130,7 +129,7 @@ public List Children private class BufferedContainerDrawNodeSharedData : BufferedDrawNodeSharedData { - public BufferedContainerDrawNodeSharedData(RenderbufferInternalFormat[] formats, bool pixelSnapping, bool clipToRootNode) + public BufferedContainerDrawNodeSharedData(RenderBufferFormat[] formats, bool pixelSnapping, bool clipToRootNode) : base(2, formats, pixelSnapping, clipToRootNode) { } diff --git a/osu.Framework/Graphics/Lines/Path.cs b/osu.Framework/Graphics/Lines/Path.cs index 4717f8f883..5294c96ffc 100644 --- a/osu.Framework/Graphics/Lines/Path.cs +++ b/osu.Framework/Graphics/Lines/Path.cs @@ -12,8 +12,8 @@ using System.Collections.Generic; using osu.Framework.Caching; using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Graphics.Rendering; using osuTK.Graphics; -using osuTK.Graphics.ES30; namespace osu.Framework.Graphics.Lines { @@ -281,7 +281,7 @@ protected Texture Texture public Color4 BackgroundColour => new Color4(0, 0, 0, 0); - private readonly BufferedDrawNodeSharedData sharedData = new BufferedDrawNodeSharedData(new[] { RenderbufferInternalFormat.DepthComponent16 }, clipToRootNode: true); + private readonly BufferedDrawNodeSharedData sharedData = new BufferedDrawNodeSharedData(new[] { RenderBufferFormat.D16 }, clipToRootNode: true); protected override DrawNode CreateDrawNode() => new BufferedDrawNode(this, new PathDrawNode(this), sharedData); diff --git a/osu.Framework/Graphics/OpenGL/OpenGLRenderer.cs b/osu.Framework/Graphics/OpenGL/OpenGLRenderer.cs index 662e180791..504957e5ac 100644 --- a/osu.Framework/Graphics/OpenGL/OpenGLRenderer.cs +++ b/osu.Framework/Graphics/OpenGL/OpenGLRenderer.cs @@ -1,15 +1,54 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Textures; using osuTK.Graphics.ES30; namespace osu.Framework.Graphics.OpenGL { public class OpenGLRenderer : IRenderer { - public IFrameBuffer CreateFrameBuffer(RenderbufferInternalFormat[]? renderBufferFormats = null, All filteringMode = All.Linear) - => new FrameBuffer(renderBufferFormats, filteringMode); + public IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) + { + All glFilteringMode; + RenderbufferInternalFormat[]? glFormats = null; + + switch (filteringMode) + { + case TextureFilteringMode.Linear: + glFilteringMode = All.Linear; + break; + + case TextureFilteringMode.Nearest: + glFilteringMode = All.Nearest; + break; + + default: + throw new ArgumentException($"Unsupported filtering mode: {filteringMode}", nameof(filteringMode)); + } + + if (renderBufferFormats != null) + { + glFormats = new RenderbufferInternalFormat[renderBufferFormats.Length]; + + for (int i = 0; i < renderBufferFormats.Length; i++) + { + switch (renderBufferFormats[i]) + { + case RenderBufferFormat.D16: + glFormats[i] = RenderbufferInternalFormat.DepthComponent16; + break; + + default: + throw new ArgumentException($"Unsupported render buffer format: {renderBufferFormats[i]}", nameof(renderBufferFormats)); + } + } + } + + return new FrameBuffer(glFormats, glFilteringMode); + } } } diff --git a/osu.Framework/Graphics/Rendering/IRenderer.cs b/osu.Framework/Graphics/Rendering/IRenderer.cs index 003490b3e3..fd7efbdaa9 100644 --- a/osu.Framework/Graphics/Rendering/IRenderer.cs +++ b/osu.Framework/Graphics/Rendering/IRenderer.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK.Graphics.ES30; +using osu.Framework.Graphics.Textures; namespace osu.Framework.Graphics.Rendering { @@ -16,6 +16,6 @@ public interface IRenderer /// Any render buffer formats. /// The texture filtering mode. /// The . - IFrameBuffer CreateFrameBuffer(RenderbufferInternalFormat[]? renderBufferFormats = null, All filteringMode = All.Linear); + IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear); } } diff --git a/osu.Framework/Graphics/Rendering/RenderBufferFormat.cs b/osu.Framework/Graphics/Rendering/RenderBufferFormat.cs new file mode 100644 index 0000000000..0569c80d65 --- /dev/null +++ b/osu.Framework/Graphics/Rendering/RenderBufferFormat.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Framework.Graphics.Rendering +{ + public enum RenderBufferFormat + { + /// + /// 16-bit depth format. + /// + D16 + } +} From 852838b20db216cd9fd4027553e6db4dcc332c39 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 30 Jul 2022 01:02:32 +0900 Subject: [PATCH 4/8] Remove more references to FrameBuffer --- osu.Framework/Graphics/BufferedDrawNode.cs | 11 +++++------ .../Graphics/BufferedDrawNodeSharedData.cs | 9 ++++----- osu.Framework/Graphics/DrawNode.cs | 5 ++--- osu.Framework/Graphics/IBufferedDrawable.cs | 14 +++++++------- osu.Framework/Graphics/Textures/TextureUpload.cs | 4 ++-- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/osu.Framework/Graphics/BufferedDrawNode.cs b/osu.Framework/Graphics/BufferedDrawNode.cs index eaf6f79426..c821689153 100644 --- a/osu.Framework/Graphics/BufferedDrawNode.cs +++ b/osu.Framework/Graphics/BufferedDrawNode.cs @@ -6,7 +6,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics.OpenGL; -using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Rendering; using osu.Framework.Statistics; @@ -20,12 +19,12 @@ public class BufferedDrawNode : TexturedShaderDrawNode protected new IBufferedDrawable Source => (IBufferedDrawable)base.Source; /// - /// The child which is used to populate the s with. + /// The child which is used to populate the s with. /// protected DrawNode Child { get; private set; } /// - /// Data shared amongst all s, providing storage for s. + /// Data shared amongst all s, providing storage for s. /// protected readonly BufferedDrawNodeSharedData SharedData; @@ -75,7 +74,7 @@ public override void ApplyState() /// /// Retrieves the version of the state of this . - /// The will only re-render if this version is greater than that of the rendered s. + /// The will only re-render if this version is greater than that of the rendered s. /// /// /// By default, the is re-rendered with every invalidation. @@ -141,9 +140,9 @@ protected virtual void DrawContents(IRenderer renderer) } /// - /// Binds and initialises a if required. + /// Binds and initialises an if required. /// - /// The to bind. + /// The to bind. /// A token that must be disposed upon finishing use of . protected IDisposable BindFrameBuffer(IFrameBuffer frameBuffer) { diff --git a/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs b/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs index 2622ddbb07..c7d6c76136 100644 --- a/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs +++ b/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Graphics.OpenGL; -using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Textures; @@ -26,7 +25,7 @@ public class BufferedDrawNodeSharedData : IDisposable internal long DrawVersion = -1; /// - /// The which contains the original version of the rendered . + /// The which contains the original version of the rendered . /// public IFrameBuffer MainBuffer { get; private set; } @@ -42,7 +41,7 @@ public class BufferedDrawNodeSharedData : IDisposable public readonly bool ClipToRootNode; /// - /// A set of s which are used in a ping-pong manner to render effects to. + /// A set of s which are used in a ping-pong manner to render effects to. /// private readonly IFrameBuffer[] effectBuffers; @@ -92,12 +91,12 @@ public void Initialise(IRenderer renderer) private int currentEffectBuffer = -1; /// - /// The which contains the most up-to-date drawn effect. + /// The which contains the most up-to-date drawn effect. /// public IFrameBuffer CurrentEffectBuffer => currentEffectBuffer == -1 ? MainBuffer : effectBuffers[currentEffectBuffer]; /// - /// Retrieves the next which effects can be rendered to. + /// Retrieves the next which effects can be rendered to. /// /// If there are no available effect buffers. public IFrameBuffer GetNextEffectBuffer() diff --git a/osu.Framework/Graphics/DrawNode.cs b/osu.Framework/Graphics/DrawNode.cs index 28d1b60f53..627f822935 100644 --- a/osu.Framework/Graphics/DrawNode.cs +++ b/osu.Framework/Graphics/DrawNode.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.OpenGL; -using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; @@ -260,9 +259,9 @@ protected void DrawClipped(ref T polygon, TextureGL texture, ColourInfo drawC } /// - /// Draws a to the screen. + /// Draws an to the screen. /// - /// The to draw. + /// The to draw. /// The destination vertices. /// The colour to draw the with. /// An action that adds vertices to a . diff --git a/osu.Framework/Graphics/IBufferedDrawable.cs b/osu.Framework/Graphics/IBufferedDrawable.cs index c7b713d6cf..3a731d43fa 100644 --- a/osu.Framework/Graphics/IBufferedDrawable.cs +++ b/osu.Framework/Graphics/IBufferedDrawable.cs @@ -3,7 +3,7 @@ #nullable disable -using osu.Framework.Graphics.OpenGL.Buffers; +using osu.Framework.Graphics.Rendering; using osuTK; using osuTK.Graphics; @@ -15,26 +15,26 @@ namespace osu.Framework.Graphics public interface IBufferedDrawable : ITexturedShaderDrawable { /// - /// The background colour of the s. + /// The background colour of the s. /// Visually changes the colour which rendered alpha is blended against. /// /// /// This should generally be transparent-black or transparent-white, but can also be used to - /// colourise the background colour of the with non-transparent colours. + /// colourise the background colour of the with non-transparent colours. /// Color4 BackgroundColour { get; } /// - /// The colour with which the s are rendered to the screen. - /// A null value implies the s should be drawn as they are. + /// The colour with which the s are rendered to the screen. + /// A null value implies the s should be drawn as they are. /// DrawColourInfo? FrameBufferDrawColour { get; } /// - /// The scale of the s drawn relative to the size of this . + /// The scale of the s drawn relative to the size of this . /// /// - /// The contents of the s are populated at this scale, however the scale of s remains unaffected. + /// The contents of the s are populated at this scale, however the scale of s remains unaffected. /// Vector2 FrameBufferScale { get; } } diff --git a/osu.Framework/Graphics/Textures/TextureUpload.cs b/osu.Framework/Graphics/Textures/TextureUpload.cs index 419f9f557c..82a708a286 100644 --- a/osu.Framework/Graphics/Textures/TextureUpload.cs +++ b/osu.Framework/Graphics/Textures/TextureUpload.cs @@ -9,8 +9,8 @@ using osu.Framework.Extensions; using osu.Framework.Extensions.ImageExtensions; using osu.Framework.Graphics.OpenGL; -using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Rendering; using osu.Framework.Logging; using osuTK.Graphics.ES30; using SixLabors.ImageSharp; @@ -109,7 +109,7 @@ internal static Image LoadFromStream(Stream stream) where TPixel } /// - /// Create an empty upload. Used by for initialisation. + /// Create an empty upload. Used by for initialisation. /// internal TextureUpload() { From c940b452459fd2b5a8441cfc6ed06c5e722c1e57 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 31 Jul 2022 13:19:13 +0900 Subject: [PATCH 5/8] Make FBOs initialise on construction --- .../Graphics/OpenGL/Buffers/FrameBuffer.cs | 84 +++++++------------ 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs b/osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs index ebb09f14f1..4a217d34b9 100644 --- a/osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs +++ b/osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs @@ -15,22 +15,30 @@ namespace osu.Framework.Graphics.OpenGL.Buffers { internal class FrameBuffer : IFrameBuffer { - public Texture Texture { get; private set; } - - private int frameBuffer = -1; + public Texture Texture { get; } private readonly List attachedRenderBuffers = new List(); - private bool isInitialised; + private int frameBuffer; private TextureGL textureGL; - private readonly All filteringMode; - private readonly RenderbufferInternalFormat[] renderBufferFormats; - public FrameBuffer(RenderbufferInternalFormat[] renderBufferFormats = null, All filteringMode = All.Linear) { - this.renderBufferFormats = renderBufferFormats; - this.filteringMode = filteringMode; + frameBuffer = GL.GenFramebuffer(); + Texture = new Texture(textureGL = new FrameBufferTexture(filteringMode)); + + GLWrapper.BindFrameBuffer(frameBuffer); + + GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget2d.Texture2D, textureGL.TextureId, 0); + GLWrapper.BindTexture(null); + + if (renderBufferFormats != null) + { + foreach (var format in renderBufferFormats) + attachedRenderBuffers.Add(new RenderBuffer(format)); + } + + GLWrapper.UnbindFrameBuffer(frameBuffer); } private Vector2 size = Vector2.One; @@ -48,31 +56,10 @@ public Vector2 Size size = value; - if (isInitialised) - { - textureGL.Width = (int)Math.Ceiling(size.X); - textureGL.Height = (int)Math.Ceiling(size.Y); - - textureGL.SetData(new TextureUpload()); - textureGL.Upload(); - } - } - } - - private void initialise() - { - frameBuffer = GL.GenFramebuffer(); - Texture = new Texture(textureGL = new FrameBufferTexture(Size, filteringMode)); - - GLWrapper.BindFrameBuffer(frameBuffer); - - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget2d.Texture2D, textureGL.TextureId, 0); - GLWrapper.BindTexture(null); - - if (renderBufferFormats != null) - { - foreach (var format in renderBufferFormats) - attachedRenderBuffers.Add(new RenderBuffer(format)); + textureGL.Width = (int)Math.Ceiling(size.X); + textureGL.Height = (int)Math.Ceiling(size.Y); + textureGL.SetData(new TextureUpload()); + textureGL.Upload(); } } @@ -82,16 +69,7 @@ private void initialise() /// public void Bind() { - if (!isInitialised) - { - initialise(); - isInitialised = true; - } - else - { - // Buffer is bound during initialisation - GLWrapper.BindFrameBuffer(frameBuffer); - } + GLWrapper.BindFrameBuffer(frameBuffer); foreach (var buffer in attachedRenderBuffers) buffer.Bind(Size); @@ -131,16 +109,14 @@ protected virtual void Dispose(bool disposing) if (isDisposed) return; - if (isInitialised) - { - textureGL?.Dispose(); - textureGL = null; + textureGL?.Dispose(); + textureGL = null; - GLWrapper.DeleteFrameBuffer(frameBuffer); + GLWrapper.DeleteFrameBuffer(frameBuffer); + frameBuffer = -1; - foreach (var buffer in attachedRenderBuffers) - buffer.Dispose(); - } + foreach (var buffer in attachedRenderBuffers) + buffer.Dispose(); isDisposed = true; } @@ -149,8 +125,8 @@ protected virtual void Dispose(bool disposing) private class FrameBufferTexture : TextureGLSingle { - public FrameBufferTexture(Vector2 size, All filteringMode = All.Linear) - : base((int)Math.Ceiling(size.X), (int)Math.Ceiling(size.Y), true, filteringMode) + public FrameBufferTexture(All filteringMode = All.Linear) + : base(1, 1, true, filteringMode) { BypassTextureUploadQueueing = true; From b573919ea42f968065427601e42a1bdc69a7afbc Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 31 Jul 2022 13:34:25 +0900 Subject: [PATCH 6/8] Remove unnecessary null check --- osu.Framework/Graphics/Sprites/BufferedContainerView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Framework/Graphics/Sprites/BufferedContainerView.cs b/osu.Framework/Graphics/Sprites/BufferedContainerView.cs index d06f3358d9..dc226563fe 100644 --- a/osu.Framework/Graphics/Sprites/BufferedContainerView.cs +++ b/osu.Framework/Graphics/Sprites/BufferedContainerView.cs @@ -139,7 +139,7 @@ public override void Draw(IRenderer renderer) { base.Draw(renderer); - if (shared?.MainBuffer?.Texture?.Available != true || shared.DrawVersion == -1) + if (shared?.MainBuffer?.Texture.Available != true || shared.DrawVersion == -1) return; Shader.Bind(); From 173d14189e957ebc146d8a6437e4e133fbb9533a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 31 Jul 2022 15:19:43 +0900 Subject: [PATCH 7/8] Fix texture being inverted --- osu.Framework/Graphics/DrawNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Framework/Graphics/DrawNode.cs b/osu.Framework/Graphics/DrawNode.cs index 627f822935..601360eccd 100644 --- a/osu.Framework/Graphics/DrawNode.cs +++ b/osu.Framework/Graphics/DrawNode.cs @@ -274,7 +274,7 @@ protected void DrawFrameBuffer(IFrameBuffer frameBuffer, Quad vertexQuad, Colour RectangleF textureRect = new RectangleF(0, frameBuffer.Texture.Height, frameBuffer.Texture.Width, -frameBuffer.Texture.Height); if (frameBuffer.Texture.TextureGL.Bind()) - DrawQuad(frameBuffer.Texture, vertexQuad, drawColour, textureRect, vertexAction, inflationPercentage, blendRangeOverride); + DrawQuad(frameBuffer.Texture.TextureGL, vertexQuad, drawColour, textureRect, vertexAction, inflationPercentage, blendRangeOverride); } /// From 92281db40efc70101e7ed854fa107bddf029623e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 31 Jul 2022 15:23:49 +0900 Subject: [PATCH 8/8] Add IsInitialised to BufferedDrawNodeSharedData --- osu.Framework/Graphics/BufferedDrawNode.cs | 3 ++- osu.Framework/Graphics/BufferedDrawNodeSharedData.cs | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Framework/Graphics/BufferedDrawNode.cs b/osu.Framework/Graphics/BufferedDrawNode.cs index c821689153..28b12d9378 100644 --- a/osu.Framework/Graphics/BufferedDrawNode.cs +++ b/osu.Framework/Graphics/BufferedDrawNode.cs @@ -84,7 +84,8 @@ public override void ApplyState() public sealed override void Draw(IRenderer renderer) { - SharedData.Initialise(renderer); + if (!SharedData.IsInitialised) + SharedData.Initialise(renderer); if (RequiresRedraw) { diff --git a/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs b/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs index c7d6c76136..63a83c6760 100644 --- a/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs +++ b/osu.Framework/Graphics/BufferedDrawNodeSharedData.cs @@ -40,6 +40,8 @@ public class BufferedDrawNodeSharedData : IDisposable /// public readonly bool ClipToRootNode; + public bool IsInitialised { get; private set; } + /// /// A set of s which are used in a ping-pong manner to render effects to. /// @@ -78,7 +80,7 @@ public BufferedDrawNodeSharedData(int effectBufferCount, RenderBufferFormat[] fo public void Initialise(IRenderer renderer) { - if (MainBuffer != null) + if (IsInitialised) return; TextureFilteringMode filterMode = PixelSnapping ? TextureFilteringMode.Nearest : TextureFilteringMode.Linear; @@ -86,6 +88,8 @@ public void Initialise(IRenderer renderer) MainBuffer = renderer.CreateFrameBuffer(formats, filterMode); for (int i = 0; i < effectBuffers.Length; i++) effectBuffers[i] = renderer.CreateFrameBuffer(formats, filterMode); + + IsInitialised = true; } private int currentEffectBuffer = -1; @@ -123,6 +127,9 @@ public void Dispose() protected virtual void Dispose(bool isDisposing) { + if (!IsInitialised) + return; + MainBuffer?.Dispose(); for (int i = 0; i < effectBuffers.Length; i++) effectBuffers[i]?.Dispose();