// 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; using osu.Framework.Graphics.OpenGL.Buffers; using osuTK.Graphics.ES30; namespace osu.Framework.Graphics { /// /// Contains data which is shared between all s of a . /// /// /// This should be constructed _once_ per , and given to the constructor of . /// public class BufferedDrawNodeSharedData : IDisposable { /// /// The version of drawn contents currently present in and . /// This should only be modified by . /// internal long DrawVersion = -1; /// /// The which contains the original version of the rendered . /// public FrameBuffer MainBuffer { get; } /// /// Whether the frame buffer position should be snapped to the nearest pixel when blitting. /// This amounts to setting the texture filtering mode to "nearest". /// public readonly bool PixelSnapping; /// /// A set of s which are used in a ping-pong manner to render effects to. /// private readonly FrameBuffer[] effectBuffers; /// /// Creates a new with no effect buffers. /// public BufferedDrawNodeSharedData(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false) : this(0, formats, pixelSnapping) { } /// /// Creates a new with a specific amount of effect buffers. /// /// The number of effect buffers. /// The render buffer formats to attach to each frame buffer. /// Whether the frame buffer position should be snapped to the nearest pixel when blitting. /// This amounts to setting the texture filtering mode to "nearest". /// If is less than 0. public BufferedDrawNodeSharedData(int effectBufferCount, RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false) { if (effectBufferCount < 0) throw new ArgumentOutOfRangeException(nameof(effectBufferCount), "Must be positive."); PixelSnapping = pixelSnapping; All filterMode = pixelSnapping ? All.Nearest : All.Linear; MainBuffer = new FrameBuffer(formats, filterMode); effectBuffers = new FrameBuffer[effectBufferCount]; for (int i = 0; i < effectBufferCount; i++) effectBuffers[i] = new FrameBuffer(formats, filterMode); } private int currentEffectBuffer = -1; /// /// The which contains the most up-to-date drawn effect. /// public FrameBuffer 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() { if (effectBuffers.Length == 0) throw new InvalidOperationException($"The {nameof(BufferedDrawNode)} requested an effect buffer, but none were available."); if (++currentEffectBuffer >= effectBuffers.Length) currentEffectBuffer = 0; return effectBuffers[currentEffectBuffer]; } /// /// Resets . /// This should only be called by . /// internal void ResetCurrentEffectBuffer() => currentEffectBuffer = -1; public void Dispose() { GLWrapper.ScheduleDisposal(() => Dispose(true)); GC.SuppressFinalize(this); } protected virtual void Dispose(bool isDisposing) { MainBuffer.Dispose(); for (int i = 0; i < effectBuffers.Length; i++) effectBuffers[i].Dispose(); } } }