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.Graphics.OpenGL;
6using osu.Framework.Graphics.OpenGL.Buffers;
7using osuTK.Graphics.ES30;
8
9namespace osu.Framework.Graphics
10{
11 /// <summary>
12 /// Contains data which is shared between all <see cref="BufferedDrawNode"/>s of a <see cref="Drawable"/>.
13 /// </summary>
14 /// <remarks>
15 /// This should be constructed _once_ per <see cref="Drawable"/>, and given to the constructor of <see cref="BufferedDrawNode"/>.
16 /// </remarks>
17 public class BufferedDrawNodeSharedData : IDisposable
18 {
19 /// <summary>
20 /// The version of drawn contents currently present in <see cref="MainBuffer"/> and <see cref="effectBuffers"/>.
21 /// This should only be modified by <see cref="BufferedDrawNode"/>.
22 /// </summary>
23 internal long DrawVersion = -1;
24
25 /// <summary>
26 /// The <see cref="FrameBuffer"/> which contains the original version of the rendered <see cref="Drawable"/>.
27 /// </summary>
28 public FrameBuffer MainBuffer { get; }
29
30 /// <summary>
31 /// Whether the frame buffer position should be snapped to the nearest pixel when blitting.
32 /// This amounts to setting the texture filtering mode to "nearest".
33 /// </summary>
34 public readonly bool PixelSnapping;
35
36 /// <summary>
37 /// A set of <see cref="FrameBuffer"/>s which are used in a ping-pong manner to render effects to.
38 /// </summary>
39 private readonly FrameBuffer[] effectBuffers;
40
41 /// <summary>
42 /// Creates a new <see cref="BufferedDrawNodeSharedData"/> with no effect buffers.
43 /// </summary>
44 public BufferedDrawNodeSharedData(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false)
45 : this(0, formats, pixelSnapping)
46 {
47 }
48
49 /// <summary>
50 /// Creates a new <see cref="BufferedDrawNodeSharedData"/> with a specific amount of effect buffers.
51 /// </summary>
52 /// <param name="effectBufferCount">The number of effect buffers.</param>
53 /// <param name="formats">The render buffer formats to attach to each frame buffer.</param>
54 /// <param name="pixelSnapping">Whether the frame buffer position should be snapped to the nearest pixel when blitting.
55 /// This amounts to setting the texture filtering mode to "nearest".</param>
56 /// <exception cref="ArgumentOutOfRangeException">If <paramref name="effectBufferCount"/> is less than 0.</exception>
57 public BufferedDrawNodeSharedData(int effectBufferCount, RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false)
58 {
59 if (effectBufferCount < 0)
60 throw new ArgumentOutOfRangeException(nameof(effectBufferCount), "Must be positive.");
61
62 PixelSnapping = pixelSnapping;
63 All filterMode = pixelSnapping ? All.Nearest : All.Linear;
64
65 MainBuffer = new FrameBuffer(formats, filterMode);
66 effectBuffers = new FrameBuffer[effectBufferCount];
67
68 for (int i = 0; i < effectBufferCount; i++)
69 effectBuffers[i] = new FrameBuffer(formats, filterMode);
70 }
71
72 private int currentEffectBuffer = -1;
73
74 /// <summary>
75 /// The <see cref="FrameBuffer"/> which contains the most up-to-date drawn effect.
76 /// </summary>
77 public FrameBuffer CurrentEffectBuffer => currentEffectBuffer == -1 ? MainBuffer : effectBuffers[currentEffectBuffer];
78
79 /// <summary>
80 /// Retrieves the next <see cref="FrameBuffer"/> which effects can be rendered to.
81 /// </summary>
82 /// <exception cref="InvalidOperationException">If there are no available effect buffers.</exception>
83 public FrameBuffer GetNextEffectBuffer()
84 {
85 if (effectBuffers.Length == 0)
86 throw new InvalidOperationException($"The {nameof(BufferedDrawNode)} requested an effect buffer, but none were available.");
87
88 if (++currentEffectBuffer >= effectBuffers.Length)
89 currentEffectBuffer = 0;
90 return effectBuffers[currentEffectBuffer];
91 }
92
93 /// <summary>
94 /// Resets <see cref="CurrentEffectBuffer"/>.
95 /// This should only be called by <see cref="BufferedDrawNode"/>.
96 /// </summary>
97 internal void ResetCurrentEffectBuffer() => currentEffectBuffer = -1;
98
99 public void Dispose()
100 {
101 GLWrapper.ScheduleDisposal(() => Dispose(true));
102 GC.SuppressFinalize(this);
103 }
104
105 protected virtual void Dispose(bool isDisposing)
106 {
107 MainBuffer.Dispose();
108
109 for (int i = 0; i < effectBuffers.Length; i++)
110 effectBuffers[i].Dispose();
111 }
112 }
113}