diff --git a/extras/JuceDemo/Source/demos/OpenGLDemo.cpp b/extras/JuceDemo/Source/demos/OpenGLDemo.cpp index 278db77bf5..5968f92ad0 100644 --- a/extras/JuceDemo/Source/demos/OpenGLDemo.cpp +++ b/extras/JuceDemo/Source/demos/OpenGLDemo.cpp @@ -60,7 +60,8 @@ public: addAndMakeVisible (&sizeSlider); sizeSlider.setBounds ("parent.width * 0.05, parent.height - 35, parent.width * 0.6, top + 24"); - openGLContext.setRenderer (this, true); + openGLContext.setRenderer (this); + openGLContext.setComponentPaintingEnabled (true); openGLContext.attachTo (*this); startTimer (1000 / 30); diff --git a/modules/juce_opengl/native/juce_OpenGL_android.h b/modules/juce_opengl/native/juce_OpenGL_android.h index f87ba1bfe2..4a96f9143a 100644 --- a/modules/juce_opengl/native/juce_OpenGL_android.h +++ b/modules/juce_opengl/native/juce_OpenGL_android.h @@ -72,7 +72,6 @@ public: } bool makeActive() const noexcept { return isInsideGLCallback; } - bool makeInactive() const noexcept { return true; } bool isActive() const noexcept { return isInsideGLCallback; } void swapBuffers() const noexcept {} diff --git a/modules/juce_opengl/native/juce_OpenGL_ios.h b/modules/juce_opengl/native/juce_OpenGL_ios.h index 64221236ea..0622da7c29 100644 --- a/modules/juce_opengl/native/juce_OpenGL_ios.h +++ b/modules/juce_opengl/native/juce_OpenGL_ios.h @@ -116,11 +116,6 @@ public: return true; } - bool makeInactive() const noexcept - { - return (! isActive()) || [EAGLContext setCurrentContext: nil]; - } - bool isActive() const noexcept { return [EAGLContext currentContext] == context; diff --git a/modules/juce_opengl/native/juce_OpenGL_linux.h b/modules/juce_opengl/native/juce_OpenGL_linux.h index e5b709d351..0a467440c9 100644 --- a/modules/juce_opengl/native/juce_OpenGL_linux.h +++ b/modules/juce_opengl/native/juce_OpenGL_linux.h @@ -114,7 +114,7 @@ public: void shutdownOnRenderThread() { - makeInactive(); + glXMakeCurrent (display, None, 0); glXDestroyContext (display, renderContext); renderContext = nullptr; } @@ -125,11 +125,6 @@ public: && glXMakeCurrent (display, embeddedWindow, renderContext); } - bool makeInactive() const noexcept - { - return (! isActive()) || glXMakeCurrent (display, None, 0); - } - bool isActive() const noexcept { return glXGetCurrentContext() == renderContext && renderContext != 0; diff --git a/modules/juce_opengl/native/juce_OpenGL_osx.h b/modules/juce_opengl/native/juce_OpenGL_osx.h index 2c6329d0da..c5cc05395d 100644 --- a/modules/juce_opengl/native/juce_OpenGL_osx.h +++ b/modules/juce_opengl/native/juce_OpenGL_osx.h @@ -36,7 +36,6 @@ - (id) initWithFrame: (NSRect) frameRect pixelFormat: (NSOpenGLPixelFormat*) format; - (bool) makeActive; -- (void) makeInactive; - (void) reshape; - (void) rightMouseDown: (NSEvent*) ev; - (void) rightMouseUp: (NSEvent*) ev; @@ -84,12 +83,6 @@ return true; } -- (void) makeInactive -{ - const juce::ScopedLock sl (*contextLock); - [NSOpenGLContext clearCurrentContext]; -} - - (void) _surfaceNeedsUpdate: (NSNotification*) notification { (void) notification; @@ -187,12 +180,6 @@ public: return true; } - bool makeInactive() const noexcept - { - [view makeInactive]; - return true; - } - bool isActive() const noexcept { return [NSOpenGLContext currentContext] == renderContext; diff --git a/modules/juce_opengl/native/juce_OpenGL_win32.h b/modules/juce_opengl/native/juce_OpenGL_win32.h index 946154ba5b..8e80a61853 100644 --- a/modules/juce_opengl/native/juce_OpenGL_win32.h +++ b/modules/juce_opengl/native/juce_OpenGL_win32.h @@ -50,7 +50,7 @@ public: initialiseGLExtensions(); const int wglFormat = wglChoosePixelFormatExtension (pixelFormat); - makeInactive(); + wglMakeCurrent (0, 0); if (wglFormat != pixFormat && wglFormat != 0) { @@ -94,11 +94,6 @@ public: return wglMakeCurrent (dc, renderContext) != FALSE; } - bool makeInactive() const noexcept - { - return (! isActive()) || (wglMakeCurrent (0, 0) != FALSE); - } - bool isActive() const noexcept { return wglGetCurrentContext() == renderContext; diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp index 6a17950334..479fba5401 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp @@ -350,11 +350,8 @@ void OpenGLContext::NativeContext::renderCallback() class OpenGLContext::Attachment : public ComponentMovementWatcher { public: - Attachment (OpenGLContext& context_, Component& comp, - const OpenGLPixelFormat& pixelFormat_, - const OpenGLContext* contextToShareWith_) - : ComponentMovementWatcher (&comp), context (context_), - pixelFormat (pixelFormat_), contextToShareWith (contextToShareWith_) + Attachment (OpenGLContext& context_, Component& comp) + : ComponentMovementWatcher (&comp), context (context_) { if (canBeAttached (comp)) attach(); @@ -418,8 +415,6 @@ public: private: OpenGLContext& context; - OpenGLPixelFormat pixelFormat; - const OpenGLContext* contextToShareWith; static bool canBeAttached (const Component& comp) noexcept { @@ -435,7 +430,8 @@ private: { Component* const comp = getComponent(); comp->setCachedComponentImage (new CachedImage (context, *comp, - pixelFormat, contextToShareWith)); + context.pixelFormat, + context.contextToShareWith)); } void detach() @@ -446,7 +442,7 @@ private: //============================================================================== OpenGLContext::OpenGLContext() - : nativeContext (nullptr), renderer (nullptr), + : nativeContext (nullptr), renderer (nullptr), contextToShareWith (nullptr), width (0), height (0), renderComponents (true) { } @@ -456,37 +452,55 @@ OpenGLContext::~OpenGLContext() detach(); } -void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse, - bool shouldAlsoPaintComponent) noexcept +void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept { // This method must not be called when the context has already been attached! // Call it before attaching your context, or use detach() first, before calling this! jassert (nativeContext == nullptr); renderer = rendererToUse; - renderComponents = shouldAlsoPaintComponent; } -void OpenGLContext::attachTo (Component& component, - const OpenGLPixelFormat& pixelFormat, - const OpenGLContext* contextToShareWith) +void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept { - if (getTargetComponent() != &component) - { - detach(); + // This method must not be called when the context has already been attached! + // Call it before attaching your context, or use detach() first, before calling this! + jassert (nativeContext == nullptr); - width = component.getWidth(); - height = component.getHeight(); + renderComponents = shouldPaintComponent; +} - attachment = new Attachment (*this, component, - pixelFormat, contextToShareWith); - } +void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept +{ + // This method must not be called when the context has already been attached! + // Call it before attaching your context, or use detach() first, before calling this! + jassert (nativeContext == nullptr); + + pixelFormat = preferredPixelFormat; +} + +void OpenGLContext::setContextToShareWith (const OpenGLContext* context) noexcept +{ + // This method must not be called when the context has already been attached! + // Call it before attaching your context, or use detach() first, before calling this! + jassert (nativeContext == nullptr); + + contextToShareWith = context; } void OpenGLContext::attachTo (Component& component) { component.repaint(); - attachTo (component, OpenGLPixelFormat(), nullptr); + + if (getTargetComponent() != &component) + { + detach(); + + width = component.getWidth(); + height = component.getHeight(); + + attachment = new Attachment (*this, component); + } } void OpenGLContext::detach() @@ -522,7 +536,6 @@ OpenGLContext* OpenGLContext::getCurrentContext() } bool OpenGLContext::makeActive() const noexcept { return nativeContext != nullptr && nativeContext->makeActive(); } -bool OpenGLContext::makeInactive() const noexcept { return nativeContext != nullptr && nativeContext->makeInactive(); } bool OpenGLContext::isActive() const noexcept { return nativeContext != nullptr && nativeContext->isActive(); } void OpenGLContext::triggerRepaint() @@ -575,7 +588,7 @@ bool OpenGLContext::areShadersAvailable() const return c != nullptr && c->shadersAvailable; } -ReferenceCountedObjectPtr OpenGLContext::getAssociatedObject (const char* name) const +ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const { jassert (name != nullptr); @@ -635,7 +648,7 @@ void OpenGLContext::copyTexture (const Rectangle& targetClipArea, static const OverlayShaderProgram& select (OpenGLContext& context) { static const char programValueID[] = "juceGLComponentOverlayShader"; - OverlayShaderProgram* program = static_cast (context.getAssociatedObject (programValueID).getObject()); + OverlayShaderProgram* program = static_cast (context.getAssociatedObject (programValueID)); if (program == nullptr) { @@ -716,7 +729,9 @@ void OpenGLContext::copyTexture (const Rectangle& targetClipArea, JUCE_CHECK_OPENGL_ERROR glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + extensions.glUseProgram (0); + extensions.glDisableVertexAttribArray (program.params.positionAttribute.attributeID); } #if JUCE_USE_OPENGL_FIXED_FUNCTION else diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.h b/modules/juce_opengl/opengl/juce_OpenGLContext.h index 6418ac674b..7a0982c301 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.h @@ -31,6 +31,10 @@ //============================================================================== /** + A base class that should be implemented by classes which want to render openGL + on a background thread. + + @see OpenGLContext */ class JUCE_API OpenGLRenderer { @@ -38,24 +42,42 @@ public: OpenGLRenderer() {} virtual ~OpenGLRenderer() {} - /** + /** Called when a new GL context has been created. + You can use this as an opportunity to create your textures, shaders, etc. + When the method is invoked, the new GL context will be active. + Note that this callback will be made on a background thread, so make sure + that your implementation is thread-safe. */ virtual void newOpenGLContextCreated() = 0; - /** + /** Called when you should render the next openGL frame. + Note that this callback will be made on a background thread, so make sure + that your implementation is thread-safe. */ virtual void renderOpenGL() = 0; - /** + /** Called when the current openGL context is about to close. + You can use this opportunity to release any GL resources that you may have + created. + + Note that this callback will be made on a background thread, so make sure + that your implementation is thread-safe. + + (Also note that on Android, this callback won't happen, because there's currently + no way to implement it..) */ virtual void openGLContextClosing() = 0; }; //============================================================================== /** - A base class for types of OpenGL context. + Creates an OpenGL context, which can be attached to a component. - An OpenGLComponent will supply its own context for drawing in its window. + To render some OpenGL in a component, you should create an instance of an OpenGLContext, + and call attachTo() to make it use your component as its render target. + To free the context, either call detach(), or delete the OpenGLContext. + + @see OpenGLRenderer */ class JUCE_API OpenGLContext { @@ -66,30 +88,66 @@ public: ~OpenGLContext(); //============================================================================== - /** */ - void setRenderer (OpenGLRenderer* rendererToUse, - bool shouldAlsoPaintComponent) noexcept; + /** Gives the context an OpenGLRenderer to use to do the drawing. + The object that you give it will not be owned by the context, so it's the caller's + responsibility to manage its lifetime and make sure that it doesn't get deleted + while the context may be using it. To stop the context using a renderer, just call + this method with a null pointer. + Note: This must be called BEFORE attaching your context to a target component! + */ + void setRenderer (OpenGLRenderer* rendererToUse) noexcept; - /** */ - void attachTo (Component& component); + /** Enables or disables the use of the GL context to perform 2D rendering + of the component to which it is attached. + If this is false, then only your OpenGLRenderer will be used to perform + any rendering. If true, then each time your target's paint() method needs + to be called, an OpenGLGraphicsContext will be used to render it, (after + calling your OpenGLRenderer if there is one). - /** */ - void attachTo (Component& component, - const OpenGLPixelFormat& preferredPixelFormat, - const OpenGLContext* contextToShareWith); - /** */ - void detach(); + By default this is set to true. If you're not using any paint() method functionality + and are doing all your rendering in an OpenGLRenderer, you should disable it + to improve performance. - /** */ - bool isAttached() const noexcept; + Note: This must be called BEFORE attaching your context to a target component! + */ + void setComponentPaintingEnabled (bool shouldPaintComponent) noexcept; + + /** Sets the pixel format which you'd like to use for the target GL surface. + Note: This must be called BEFORE attaching your context to a target component! + */ + void setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept; + + /** Provides a context with which you'd like this context's resources to be shared. + The object passed-in here must not be deleted while the context may still be + using it! To turn off sharing, you can call this method with a null pointer. + Note: This must be called BEFORE attaching your context to a target component! + */ + void setContextToShareWith (const OpenGLContext* contextToShareWith) noexcept; //============================================================================== - /** Makes this context the currently active one. */ - bool makeActive() const noexcept; - /** If this context is currently active, it is disactivated. */ - bool makeInactive() const noexcept; - /** Returns true if this context is currently active. */ - bool isActive() const noexcept; + /** Attaches the context to a target component. + + If the component is not fully visible, this call will wait until the component + is shown before actually creating a native context for it. + + When a native context is created, a thread is started, and will be used to call + the OpenGLRenderer methods. The context will be floated above the target component, + and when the target moves, it will track it. If the component is hidden/shown, the + context may be deleted and re-created. + */ + void attachTo (Component& component); + + /** Detaches the context from its target component and deletes any native resources. + If the context has not been attached, this will do nothing. Otherwise, it will block + until the context and its thread have been cleaned up. + */ + void detach(); + + /** Returns true if the context is attached to a component and is on-screen. + Note that if you call attachTo() for a non-visible component, this method will + return false until the component is made visible. + */ + bool isAttached() const noexcept; /** Returns the component to which this context is currently attached, or nullptr. */ Component* getTargetComponent() const noexcept; @@ -99,6 +157,56 @@ public: */ static OpenGLContext* getCurrentContext(); + /** Asynchronously causes a repaint to be made. */ + void triggerRepaint(); + + //============================================================================== + /** Returns the width of this context */ + inline int getWidth() const noexcept { return width; } + + /** Returns the height of this context */ + inline int getHeight() const noexcept { return height; } + + /** If this context is backed by a frame buffer, this returns its ID number, + or 0 if the context does not use a framebuffer. + */ + unsigned int getFrameBufferID() const noexcept; + + /** Returns true if shaders can be used in this context. */ + bool areShadersAvailable() const; + + /** This structure holds a set of dynamically loaded GL functions for use on this context. */ + OpenGLExtensionFunctions extensions; + + //============================================================================== + /** This retrieves an object that was previously stored with setAssociatedObject(). + If no object is found with the given name, this will return nullptr. + This method must only be called from within the GL rendering methods. + @see setAssociatedObject + */ + ReferenceCountedObject* getAssociatedObject (const char* name) const; + + /** Attaches a named object to the context, which will be deleted when the context is + destroyed. + + This allows you to store an object which will be released before the context is + deleted. The main purpose is for caching GL objects such as shader programs, which + will become invalid when the context is deleted. + + This method must only be called from within the GL rendering methods. + */ + void setAssociatedObject (const char* name, ReferenceCountedObject* newObject); + + //============================================================================== + /** Makes this context the currently active one. + You should never need to call this in normal use - the context will already be + active when OpenGLRenderer::renderOpenGL() is invoked. + */ + bool makeActive() const noexcept; + + /** Returns true if this context is currently active for the calling thread. */ + bool isActive() const noexcept; + //============================================================================== /** Swaps the buffers (if the context can do this). There's normally no need to call this directly - the buffers will be swapped @@ -122,45 +230,6 @@ public: */ int getSwapInterval() const; - /** */ - void triggerRepaint(); - - //============================================================================== - /** Returns the width of this context */ - inline int getWidth() const noexcept { return width; } - - /** Returns the height of this context */ - inline int getHeight() const noexcept { return height; } - - /** If this context is backed by a frame buffer, this returns its ID number, - or 0 if the context has no accessible framebuffer. - */ - unsigned int getFrameBufferID() const noexcept; - - /** Returns true if shaders can be used in this context. */ - bool areShadersAvailable() const; - - /** This structure holds a set of dynamically loaded GL functions for use on this context. */ - OpenGLExtensionFunctions extensions; - - /** This retrieves an object that was previously stored with setAssociatedObject(). - If no object is found with the given name, this will return nullptr. - This method must only be called from within the GL rendering methods. - @see setAssociatedObject - */ - ReferenceCountedObjectPtr getAssociatedObject (const char* name) const; - - /** Attaches a named object to the context, which will be deleted when the context is - destroyed. - - This allows you to store an object which will be released before the context is - deleted. The main purpose is for caching GL objects such as shader programs, which - will become invalid when the context is deleted. - - This method must only be called from within the GL rendering methods. - */ - void setAssociatedObject (const char* name, ReferenceCountedObject* newObject); - //============================================================================== /** Returns an OS-dependent handle to some kind of underlting OS-provided GL context. @@ -197,6 +266,8 @@ private: NativeContext* nativeContext; OpenGLRenderer* renderer; ScopedPointer attachment; + OpenGLPixelFormat pixelFormat; + const OpenGLContext* contextToShareWith; int width, height; bool renderComponents; diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index 8f9763ab49..5fb76a82d7 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -1021,7 +1021,7 @@ struct StateHelpers GLuint colour; }; - #if ! JUCE_MAC + #if ! (JUCE_MAC || JUCE_ANDROID || JUCE_IOS) enum { numQuads = 64 }; // (had problems with my drivers segfaulting when these buffers are any larger) #else enum { numQuads = 8192 }; @@ -1051,7 +1051,7 @@ struct StateHelpers activeShader (nullptr) { const char programValueID[] = "GraphicsContextPrograms"; - programs = static_cast (context.getAssociatedObject (programValueID).getObject()); + programs = static_cast (context.getAssociatedObject (programValueID)); if (programs == nullptr) { @@ -1154,6 +1154,9 @@ public: #if defined (GL_INDEX_ARRAY) glDisableClientState (GL_INDEX_ARRAY); #endif + + target.context.extensions.glBindBuffer (GL_ARRAY_BUFFER, 0); + target.context.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); } void flush() diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp index 8d638998b6..af85a6890d 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp @@ -142,7 +142,9 @@ void OpenGLTexture::release() { if (textureID != 0) { - glDeleteTextures (1, &textureID); + if (OpenGLHelpers::isContextActive()) + glDeleteTextures (1, &textureID); + textureID = 0; width = 0; height = 0;