A game framework written with osu! in mind.
at master 170 lines 5.2 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using System.Collections.Generic; 6using osu.Framework.Graphics.OpenGL.Textures; 7using osu.Framework.Graphics.Textures; 8using osuTK; 9using osuTK.Graphics.ES30; 10 11namespace osu.Framework.Graphics.OpenGL.Buffers 12{ 13 public class FrameBuffer : IDisposable 14 { 15 private int frameBuffer = -1; 16 17 public TextureGL Texture { get; private set; } 18 19 private readonly List<RenderBuffer> attachedRenderBuffers = new List<RenderBuffer>(); 20 21 private bool isInitialised; 22 23 private readonly All filteringMode; 24 private readonly RenderbufferInternalFormat[] renderBufferFormats; 25 26 public FrameBuffer(RenderbufferInternalFormat[] renderBufferFormats = null, All filteringMode = All.Linear) 27 { 28 this.renderBufferFormats = renderBufferFormats; 29 this.filteringMode = filteringMode; 30 } 31 32 private Vector2 size = Vector2.One; 33 34 /// <summary> 35 /// Sets the size of the texture of this frame buffer. 36 /// </summary> 37 public Vector2 Size 38 { 39 get => size; 40 set 41 { 42 if (value == size) 43 return; 44 45 size = value; 46 47 if (isInitialised) 48 { 49 Texture.Width = (int)Math.Ceiling(size.X); 50 Texture.Height = (int)Math.Ceiling(size.Y); 51 52 Texture.SetData(new TextureUpload()); 53 Texture.Upload(); 54 } 55 } 56 } 57 58 private void initialise() 59 { 60 frameBuffer = GL.GenFramebuffer(); 61 Texture = new FrameBufferTexture(Size, filteringMode); 62 63 GLWrapper.BindFrameBuffer(frameBuffer); 64 65 GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget2d.Texture2D, Texture.TextureId, 0); 66 GLWrapper.BindTexture(null); 67 68 if (renderBufferFormats != null) 69 { 70 foreach (var format in renderBufferFormats) 71 attachedRenderBuffers.Add(new RenderBuffer(format)); 72 } 73 } 74 75 /// <summary> 76 /// Binds the framebuffer. 77 /// <para>Does not clear the buffer or reset the viewport/ortho.</para> 78 /// </summary> 79 public void Bind() 80 { 81 if (!isInitialised) 82 { 83 initialise(); 84 isInitialised = true; 85 } 86 else 87 { 88 // Buffer is bound during initialisation 89 GLWrapper.BindFrameBuffer(frameBuffer); 90 } 91 92 foreach (var buffer in attachedRenderBuffers) 93 buffer.Bind(Size); 94 } 95 96 /// <summary> 97 /// Unbinds the framebuffer. 98 /// </summary> 99 public void Unbind() 100 { 101 // See: https://community.arm.com/developer/tools-software/graphics/b/blog/posts/mali-performance-2-how-to-correctly-handle-framebuffers 102 // Unbinding renderbuffers causes an invalidation of the relevant attachment of this framebuffer on embedded devices, causing the renderbuffers to remain transient. 103 // This must be done _before_ the framebuffer is flushed via the framebuffer unbind process, otherwise the renderbuffer may be copied to system memory. 104 foreach (var buffer in attachedRenderBuffers) 105 buffer.Unbind(); 106 107 GLWrapper.UnbindFrameBuffer(frameBuffer); 108 } 109 110 #region Disposal 111 112 ~FrameBuffer() 113 { 114 GLWrapper.ScheduleDisposal(() => Dispose(false)); 115 } 116 117 public void Dispose() 118 { 119 Dispose(true); 120 GC.SuppressFinalize(this); 121 } 122 123 private bool isDisposed; 124 125 protected virtual void Dispose(bool disposing) 126 { 127 if (isDisposed) 128 return; 129 130 if (isInitialised) 131 { 132 Texture?.Dispose(); 133 Texture = null; 134 135 GLWrapper.DeleteFrameBuffer(frameBuffer); 136 137 foreach (var buffer in attachedRenderBuffers) 138 buffer.Dispose(); 139 } 140 141 isDisposed = true; 142 } 143 144 #endregion 145 146 private class FrameBufferTexture : TextureGLSingle 147 { 148 public FrameBufferTexture(Vector2 size, All filteringMode = All.Linear) 149 : base((int)Math.Ceiling(size.X), (int)Math.Ceiling(size.Y), true, filteringMode) 150 { 151 BypassTextureUploadQueueing = true; 152 153 SetData(new TextureUpload()); 154 Upload(); 155 } 156 157 public override int Width 158 { 159 get => base.Width; 160 set => base.Width = Math.Clamp(value, 1, GLWrapper.MaxTextureSize); 161 } 162 163 public override int Height 164 { 165 get => base.Height; 166 set => base.Height = Math.Clamp(value, 1, GLWrapper.MaxTextureSize); 167 } 168 } 169 } 170}