From 29af89c72a8372dccea0cae478c84d1cb5f24c1a Mon Sep 17 00:00:00 2001 From: reuk Date: Wed, 25 Jun 2025 15:12:35 +0100 Subject: [PATCH] OpenGLFrameBuffer: Refactor pimpl to represent mutually exclusive states with std::variant --- .../opengl/juce_OpenGLFrameBuffer.cpp | 172 ++++++++++-------- 1 file changed, 101 insertions(+), 71 deletions(-) diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index 89e6401670..7f1f879aa8 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -46,20 +46,20 @@ public: bool isValid() const noexcept { - return transientState != nullptr; + return std::holds_alternative (state); } bool initialise (OpenGLContext& context, int width, int height) { jassert (context.isActive()); // The context must be active when creating a framebuffer! - transientState.reset(); - transientState.reset (new TransientState (context, width, height, false, false)); + release(); + auto& transientState = state.emplace (context, width, height, false, false); - if (! transientState->createdOk()) - transientState.reset(); + if (! transientState.createdOk()) + release(); - return transientState != nullptr; + return isValid(); } bool initialise (OpenGLContext& context, const Image& image) @@ -75,123 +75,137 @@ public: bool initialise (OpenGLFrameBuffer& other) { - auto* p = other.pimpl->transientState.get(); + auto* p = std::get_if (&other.pimpl->state); if (p == nullptr) { - transientState.reset(); + release(); return true; } - const Rectangle area (transientState->width, transientState->height); + const Rectangle area (p->width, p->height); - if (initialise (p->context, area.getWidth(), area.getHeight())) + if (! initialise (p->context, area.getWidth(), area.getHeight())) + return false; + + auto* transientState = std::get_if (&state); + transientState->bind(); + + #if ! JUCE_ANDROID + if (! transientState->context.isCoreProfile()) + glEnable (GL_TEXTURE_2D); + + clearGLError(); + #endif { - transientState->bind(); - - #if ! JUCE_ANDROID - if (! transientState->context.isCoreProfile()) - glEnable (GL_TEXTURE_2D); - - clearGLError(); - #endif - { - const ScopedTextureBinding scopedTextureBinding; - glBindTexture (GL_TEXTURE_2D, p->textureID); - transientState->context.copyTexture (area, area, area.getWidth(), area.getHeight(), false); - } - - transientState->unbind(); - return true; + const ScopedTextureBinding scopedTextureBinding; + glBindTexture (GL_TEXTURE_2D, p->textureID); + transientState->context.copyTexture (area, area, area.getWidth(), area.getHeight(), false); } - return false; + transientState->unbind(); + return true; } void release() { - transientState.reset(); - savedState.reset(); + state.emplace(); } void saveAndRelease() { - if (transientState != nullptr) + if (auto* transientState = std::get_if (&state)) { - savedState.reset(); - if (auto toSave = readPixels ({ transientState->width, transientState->height })) - savedState = std::make_unique (std::move (*toSave)); - - transientState.reset(); + state.emplace (std::move (*toSave)); } } bool reloadSavedCopy (OpenGLContext& context) { - if (savedState != nullptr) + if (auto* savedState = std::get_if (&state)) { - std::unique_ptr state; - std::swap (state, savedState); + auto local = std::move (*savedState); - if (restore (context, *state)) + if (restore (context, local)) return true; - std::swap (state, savedState); + state.emplace (std::move (local)); } return false; } - int getWidth() const noexcept { return transientState != nullptr ? transientState->width : 0; } - int getHeight() const noexcept { return transientState != nullptr ? transientState->height : 0; } - GLuint getTextureID() const noexcept { return transientState != nullptr ? transientState->textureID : 0; } - - bool makeCurrentRenderingTarget() const + int getWidth() const noexcept { - // trying to use a framebuffer after saving it with saveAndRelease()! Be sure to call - // reloadSavedCopy() to put it back into GPU memory before using it.. - jassert (savedState == nullptr); + if (auto* transientState = std::get_if (&state)) + return transientState->width; - if (transientState == nullptr) - return false; + return 0; + } - transientState->bind(); - return true; + int getHeight() const noexcept + { + if (auto* transientState = std::get_if (&state)) + return transientState->height; + + return 0; + } + + GLuint getTextureID() const noexcept + { + if (auto* transientState = std::get_if (&state)) + return transientState->textureID; + + return 0; } GLuint getFrameBufferID() const noexcept { - return transientState != nullptr ? transientState->frameBufferID : 0; + if (auto* transientState = std::get_if (&state)) + return transientState->frameBufferID; + + return 0; + } + + bool makeCurrentRenderingTarget() + { + return makeAndGetCurrentRenderingTarget() != nullptr; } void releaseAsRenderingTarget() { - if (transientState != nullptr) + if (auto* transientState = std::get_if (&state)) transientState->unbind(); } void clear (Colour colour) { - if (makeCurrentRenderingTarget()) - { - OpenGLHelpers::clear (colour); - releaseAsRenderingTarget(); - } + auto* transientState = makeAndGetCurrentRenderingTarget(); + + if (transientState == nullptr) + return; + + OpenGLHelpers::clear (colour); + transientState->unbind(); } void makeCurrentAndClear() { - if (makeCurrentRenderingTarget()) - { - glClearColor (0, 0, 0, 0); - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - } + auto* transientState = makeAndGetCurrentRenderingTarget(); + + if (transientState == nullptr) + return; + + glClearColor (0, 0, 0, 0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } - bool readPixels (PixelARGB* target, const Rectangle& area) const + bool readPixels (PixelARGB* target, const Rectangle& area) { - if (! makeCurrentRenderingTarget()) + auto* transientState = makeAndGetCurrentRenderingTarget(); + + if (transientState == nullptr) return false; glPixelStorei (GL_PACK_ALIGNMENT, 4); @@ -204,11 +218,13 @@ public: bool writePixels (const PixelARGB* data, const Rectangle& area) { - OpenGLTargetSaver ts (transientState->context); + auto* transientState = makeAndGetCurrentRenderingTarget(); - if (! makeCurrentRenderingTarget()) + if (transientState == nullptr) return false; + OpenGLTargetSaver ts (transientState->context); + glDisable (GL_DEPTH_TEST); glDisable (GL_BLEND); JUCE_CHECK_OPENGL_ERROR @@ -378,7 +394,7 @@ private: return true; } - std::optional readPixels (const Rectangle& area) const + std::optional readPixels (const Rectangle& area) { SavedState result { area.getWidth(), area.getHeight(), @@ -390,8 +406,22 @@ private: return result; } - std::unique_ptr transientState; - std::unique_ptr savedState; + TransientState* makeAndGetCurrentRenderingTarget() + { + if (auto* transientState = std::get_if (&state)) + { + transientState->bind(); + return transientState; + } + + // trying to use a framebuffer after saving it with saveAndRelease()! Be sure to call + // reloadSavedCopy() to put it back into GPU memory before using it.. + jassertfalse; + + return nullptr; + } + + std::variant state; }; //==============================================================================