A game framework written with osu! in mind.
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 osu.Framework.Platform;
6using osuTK;
7using osuTK.Graphics.ES30;
8
9namespace osu.Framework.Graphics.OpenGL.Buffers
10{
11 internal class RenderBuffer : IDisposable
12 {
13 private readonly RenderbufferInternalFormat format;
14 private readonly int renderBuffer;
15 private readonly int sizePerPixel;
16
17 private FramebufferAttachment attachment;
18
19 public RenderBuffer(RenderbufferInternalFormat format)
20 {
21 this.format = format;
22
23 renderBuffer = GL.GenRenderbuffer();
24
25 GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, renderBuffer);
26
27 // OpenGL docs don't specify that this is required, but seems to be required on some platforms
28 // to correctly attach in the GL.FramebufferRenderbuffer() call below
29 GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, format, 1, 1);
30
31 attachment = format.GetAttachmentType();
32 sizePerPixel = format.GetBytesPerPixel();
33
34 GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, attachment, RenderbufferTarget.Renderbuffer, renderBuffer);
35 }
36
37 private Vector2 internalSize;
38 private NativeMemoryTracker.NativeMemoryLease memoryLease;
39
40 public void Bind(Vector2 size)
41 {
42 size = Vector2.Clamp(size, Vector2.One, new Vector2(GLWrapper.MaxRenderBufferSize));
43
44 // See: https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_multisampled_render_to_texture.txt
45 // + https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html
46 // OpenGL ES allows the driver to discard renderbuffer contents after they are presented to the screen, so the storage must always be re-initialised for embedded devices.
47 // Such discard does not exist on non-embedded platforms, so they are only re-initialised when required.
48 if (GLWrapper.IsEmbedded || internalSize.X < size.X || internalSize.Y < size.Y)
49 {
50 GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, renderBuffer);
51 GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, format, (int)Math.Ceiling(size.X), (int)Math.Ceiling(size.Y));
52
53 if (!GLWrapper.IsEmbedded)
54 {
55 memoryLease?.Dispose();
56 memoryLease = NativeMemoryTracker.AddMemory(this, (long)(size.X * size.Y * sizePerPixel));
57 }
58
59 internalSize = size;
60 }
61 }
62
63 public void Unbind()
64 {
65 if (GLWrapper.IsEmbedded)
66 {
67 // Renderbuffers are not automatically discarded on all embedded devices, so invalidation is forced for extra performance and to unify logic between devices.
68 GL.InvalidateFramebuffer(FramebufferTarget.Framebuffer, 1, ref attachment);
69 }
70 }
71
72 #region Disposal
73
74 ~RenderBuffer()
75 {
76 GLWrapper.ScheduleDisposal(() => Dispose(false));
77 }
78
79 public void Dispose()
80 {
81 Dispose(true);
82 GC.SuppressFinalize(this);
83 }
84
85 private bool isDisposed;
86
87 protected virtual void Dispose(bool disposing)
88 {
89 if (isDisposed)
90 return;
91
92 if (renderBuffer != -1)
93 {
94 memoryLease?.Dispose();
95 GL.DeleteRenderbuffer(renderBuffer);
96 }
97
98 isDisposed = true;
99 }
100
101 #endregion
102 }
103}