diff --git a/modules/juce_opengl/native/juce_OpenGL_osx.h b/modules/juce_opengl/native/juce_OpenGL_osx.h index 517179b155..cc8d0c0878 100644 --- a/modules/juce_opengl/native/juce_OpenGL_osx.h +++ b/modules/juce_opengl/native/juce_OpenGL_osx.h @@ -23,87 +23,6 @@ ============================================================================== */ -struct ThreadSafeNSOpenGLViewClass : public ObjCClass -{ - ThreadSafeNSOpenGLViewClass() : ObjCClass ("JUCEGLView_") - { - addIvar ("lock"); - addIvar ("needsUpdate"); - - addMethod (@selector (update), update, "v@:"); - addMethod (@selector (reshape), reshape, "v@:"); - addMethod (@selector (_surfaceNeedsUpdate:), surfaceNeedsUpdate, "v@:@"); - addMethod (@selector (rightMouseDown:), rightMouseDown, "v@:@"); - addMethod (@selector (rightMouseUp:), rightMouseUp, "v@:@"); - addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "v@:@"); - - registerClass(); - } - - static void init (id self) - { - object_setInstanceVariable (self, "lock", new CriticalSection()); - - #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) - if ([self respondsToSelector: @selector (setWantsBestResolutionOpenGLSurface:)]) - [self setWantsBestResolutionOpenGLSurface: YES]; - #endif - - setNeedsUpdate (self, YES); - } - - static bool makeActive (id self) - { - const ScopedLock sl (*getLock (self)); - - if ([(NSOpenGLView*) self openGLContext] == nil) - return false; - - [[(NSOpenGLView*) self openGLContext] makeCurrentContext]; - - if (getIvar (self, "needsUpdate")) - { - sendSuperclassMessage (self, @selector (update)); - setNeedsUpdate (self, NO); - } - - return true; - } - -private: - static CriticalSection* getLock (id self) - { - return getIvar (self, "lock"); - } - - static void setNeedsUpdate (id self, BOOL b) - { - object_setInstanceVariable (self, "needsUpdate", (void*) b); - } - - static void setNeedsUpdateLocked (id self, BOOL b) - { - const ScopedLock sl (*getLock (self)); - setNeedsUpdate (self, b); - } - - static void dealloc (id self, SEL) - { - delete getLock (self); - sendSuperclassMessage (self, @selector (dealloc)); - } - - static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; } - static void surfaceNeedsUpdate (id self, SEL, NSNotification*) { setNeedsUpdateLocked (self, YES); } - static void update (id self, SEL) { setNeedsUpdateLocked (self, YES); } - static void reshape (id self, SEL) { setNeedsUpdateLocked (self, YES); } - - static void rightMouseDown (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseDown: ev]; } - static void rightMouseUp (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseUp: ev]; } -}; - - -//============================================================================== class OpenGLContext::NativeContext { public: @@ -130,10 +49,14 @@ public: NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs]; - static ThreadSafeNSOpenGLViewClass cls; + static MouseForwardingNSOpenGLViewClass cls; view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) pixelFormat: format]; - ThreadSafeNSOpenGLViewClass::init (view); + + #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) + if ([view respondsToSelector: @selector (setWantsBestResolutionOpenGLSurface:)]) + [view setWantsBestResolutionOpenGLSurface: YES]; + #endif [[NSNotificationCenter defaultCenter] addObserver: view selector: @selector (_surfaceNeedsUpdate:) @@ -172,8 +95,13 @@ public: if ([renderContext view] != view) [renderContext setView: view]; - ThreadSafeNSOpenGLViewClass::makeActive (view); - return true; + if (NSOpenGLContext* context = [view openGLContext]) + { + [context makeCurrentContext]; + return true; + } + + return false; } bool isActive() const noexcept @@ -225,11 +153,29 @@ public: return numFrames; } -private: NSOpenGLContext* renderContext; NSOpenGLView* view; ReferenceCountedObjectPtr viewAttachment; + //============================================================================== + struct MouseForwardingNSOpenGLViewClass : public ObjCClass + { + MouseForwardingNSOpenGLViewClass() : ObjCClass ("JUCEGLView_") + { + addMethod (@selector (rightMouseDown:), rightMouseDown, "v@:@"); + addMethod (@selector (rightMouseUp:), rightMouseUp, "v@:@"); + addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "v@:@"); + + registerClass(); + } + + private: + static void rightMouseDown (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseDown: ev]; } + static void rightMouseUp (id self, SEL, NSEvent* ev) { [[(NSOpenGLView*) self superview] rightMouseUp: ev]; } + static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; } + }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext) }; diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp index da224e3efc..a57d88ccee 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp @@ -37,6 +37,7 @@ public: #else shadersAvailable (false), #endif + hasInitialised (false), needsUpdate (1) { nativeContext = new NativeContext (component, pixFormat, contextToShare); @@ -65,6 +66,7 @@ public: #if ! JUCE_ANDROID stopThread (10000); #endif + hasInitialised = false; } //============================================================================== @@ -276,6 +278,19 @@ public: #endif } + void handleResize() + { + updateViewportSize (true); + + #if JUCE_MAC + if (hasInitialised) + { + [nativeContext->view update]; + renderFrame(); + } + #endif + } + //============================================================================== void run() { @@ -286,15 +301,14 @@ public: return; } - nativeContext->makeActive(); - nativeContext->setSwapInterval (1); - initialiseOnThread(); #if JUCE_USE_OPENGL_SHADERS && ! JUCE_OPENGL_ES shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0; #endif + hasInitialised = true; + while (! threadShouldExit()) { if (! renderFrame()) @@ -306,15 +320,15 @@ public: void initialiseOnThread() { - associatedObjectNames.clear(); - associatedObjects.clear(); - - cachedImageFrameBuffer.release(); + jassert (associatedObjectNames.size() == 0); + jassert (! cachedImageFrameBuffer.isValid()); + context.makeActive(); nativeContext->initialiseOnRenderThread(); glViewport (0, 0, component.getWidth(), component.getHeight()); context.extensions.initialise(); + nativeContext->setSwapInterval (1); if (context.renderer != nullptr) context.renderer->newOpenGLContextCreated(); @@ -353,7 +367,7 @@ public: ReferenceCountedArray associatedObjects; WaitableEvent canPaintNowFlag, finishedPaintingFlag; - bool shadersAvailable; + bool shadersAvailable, hasInitialised; Atomic needsUpdate; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage) @@ -411,7 +425,7 @@ public: && context.nativeContext != nullptr) { if (CachedImage* const c = CachedImage::get (comp)) - c->updateViewportSize (true); + c->handleResize(); context.nativeContext->updateWindowPosition (comp.getTopLevelComponent() ->getLocalArea (&comp, comp.getLocalBounds())); @@ -562,22 +576,37 @@ Component* OpenGLContext::getTargetComponent() const noexcept return attachment != nullptr ? attachment->getComponent() : nullptr; } +static ThreadLocalValue currentThreadActiveContext; + OpenGLContext* OpenGLContext::getCurrentContext() { - #if JUCE_ANDROID - if (NativeContext* const nc = NativeContext::getActiveContext()) - if (CachedImage* currentContext = CachedImage::get (nc->component)) - #else - if (CachedImage* currentContext = dynamic_cast (Thread::getCurrentThread())) - #endif - return ¤tContext->context; - - return nullptr; + return currentThreadActiveContext.get(); } -bool OpenGLContext::makeActive() const noexcept { return nativeContext != nullptr && nativeContext->makeActive(); } -bool OpenGLContext::isActive() const noexcept { return nativeContext != nullptr && nativeContext->isActive(); } -void OpenGLContext::deactivateCurrentContext() { NativeContext::deactivateCurrentContext(); } +bool OpenGLContext::makeActive() const noexcept +{ + OpenGLContext*& current = currentThreadActiveContext.get(); + + if (nativeContext != nullptr && nativeContext->makeActive()) + { + current = const_cast (this); + return true; + } + + current = nullptr; + return false; +} + +bool OpenGLContext::isActive() const noexcept +{ + return nativeContext != nullptr && nativeContext->isActive(); +} + +void OpenGLContext::deactivateCurrentContext() +{ + NativeContext::deactivateCurrentContext(); + currentThreadActiveContext.get() = nullptr; +} void OpenGLContext::triggerRepaint() { diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index 2bf1e90ffe..9b60f72ed3 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -26,16 +26,11 @@ class OpenGLFrameBuffer::Pimpl { public: - Pimpl (OpenGLContext& context_, const int width_, const int height_, + Pimpl (OpenGLContext& c, const int w, const int h, const bool wantsDepthBuffer, const bool wantsStencilBuffer) - : context (context_), - width (width_), - height (height_), - textureID (0), - frameBufferID (0), - depthOrStencilBuffer (0), - hasDepthBuffer (false), - hasStencilBuffer (false) + : context (c), width (w), height (h), + textureID (0), frameBufferID (0), depthOrStencilBuffer (0), + hasDepthBuffer (false), hasStencilBuffer (false) { // Framebuffer objects can only be created when the current thread has an active OpenGL // context. You'll need to create this object in one of the OpenGLContext's callbacks.