| @@ -23,87 +23,6 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| */ | */ | ||||
| struct ThreadSafeNSOpenGLViewClass : public ObjCClass <NSOpenGLView> | |||||
| { | |||||
| ThreadSafeNSOpenGLViewClass() : ObjCClass <NSOpenGLView> ("JUCEGLView_") | |||||
| { | |||||
| addIvar <CriticalSection*> ("lock"); | |||||
| addIvar <BOOL> ("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<void*> (self, "needsUpdate")) | |||||
| { | |||||
| sendSuperclassMessage (self, @selector (update)); | |||||
| setNeedsUpdate (self, NO); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| private: | |||||
| static CriticalSection* getLock (id self) | |||||
| { | |||||
| return getIvar<CriticalSection*> (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 | class OpenGLContext::NativeContext | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -130,10 +49,14 @@ public: | |||||
| NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs]; | NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs]; | ||||
| static ThreadSafeNSOpenGLViewClass cls; | |||||
| static MouseForwardingNSOpenGLViewClass cls; | |||||
| view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) | view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) | ||||
| pixelFormat: format]; | 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 | [[NSNotificationCenter defaultCenter] addObserver: view | ||||
| selector: @selector (_surfaceNeedsUpdate:) | selector: @selector (_surfaceNeedsUpdate:) | ||||
| @@ -172,8 +95,13 @@ public: | |||||
| if ([renderContext view] != view) | if ([renderContext view] != view) | ||||
| [renderContext setView: view]; | [renderContext setView: view]; | ||||
| ThreadSafeNSOpenGLViewClass::makeActive (view); | |||||
| return true; | |||||
| if (NSOpenGLContext* context = [view openGLContext]) | |||||
| { | |||||
| [context makeCurrentContext]; | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | } | ||||
| bool isActive() const noexcept | bool isActive() const noexcept | ||||
| @@ -225,11 +153,29 @@ public: | |||||
| return numFrames; | return numFrames; | ||||
| } | } | ||||
| private: | |||||
| NSOpenGLContext* renderContext; | NSOpenGLContext* renderContext; | ||||
| NSOpenGLView* view; | NSOpenGLView* view; | ||||
| ReferenceCountedObjectPtr<ReferenceCountedObject> viewAttachment; | ReferenceCountedObjectPtr<ReferenceCountedObject> viewAttachment; | ||||
| //============================================================================== | |||||
| struct MouseForwardingNSOpenGLViewClass : public ObjCClass <NSOpenGLView> | |||||
| { | |||||
| MouseForwardingNSOpenGLViewClass() : ObjCClass <NSOpenGLView> ("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) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext) | ||||
| }; | }; | ||||
| @@ -37,6 +37,7 @@ public: | |||||
| #else | #else | ||||
| shadersAvailable (false), | shadersAvailable (false), | ||||
| #endif | #endif | ||||
| hasInitialised (false), | |||||
| needsUpdate (1) | needsUpdate (1) | ||||
| { | { | ||||
| nativeContext = new NativeContext (component, pixFormat, contextToShare); | nativeContext = new NativeContext (component, pixFormat, contextToShare); | ||||
| @@ -65,6 +66,7 @@ public: | |||||
| #if ! JUCE_ANDROID | #if ! JUCE_ANDROID | ||||
| stopThread (10000); | stopThread (10000); | ||||
| #endif | #endif | ||||
| hasInitialised = false; | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -276,6 +278,19 @@ public: | |||||
| #endif | #endif | ||||
| } | } | ||||
| void handleResize() | |||||
| { | |||||
| updateViewportSize (true); | |||||
| #if JUCE_MAC | |||||
| if (hasInitialised) | |||||
| { | |||||
| [nativeContext->view update]; | |||||
| renderFrame(); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void run() | void run() | ||||
| { | { | ||||
| @@ -286,15 +301,14 @@ public: | |||||
| return; | return; | ||||
| } | } | ||||
| nativeContext->makeActive(); | |||||
| nativeContext->setSwapInterval (1); | |||||
| initialiseOnThread(); | initialiseOnThread(); | ||||
| #if JUCE_USE_OPENGL_SHADERS && ! JUCE_OPENGL_ES | #if JUCE_USE_OPENGL_SHADERS && ! JUCE_OPENGL_ES | ||||
| shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0; | shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0; | ||||
| #endif | #endif | ||||
| hasInitialised = true; | |||||
| while (! threadShouldExit()) | while (! threadShouldExit()) | ||||
| { | { | ||||
| if (! renderFrame()) | if (! renderFrame()) | ||||
| @@ -306,15 +320,15 @@ public: | |||||
| void initialiseOnThread() | void initialiseOnThread() | ||||
| { | { | ||||
| associatedObjectNames.clear(); | |||||
| associatedObjects.clear(); | |||||
| cachedImageFrameBuffer.release(); | |||||
| jassert (associatedObjectNames.size() == 0); | |||||
| jassert (! cachedImageFrameBuffer.isValid()); | |||||
| context.makeActive(); | |||||
| nativeContext->initialiseOnRenderThread(); | nativeContext->initialiseOnRenderThread(); | ||||
| glViewport (0, 0, component.getWidth(), component.getHeight()); | glViewport (0, 0, component.getWidth(), component.getHeight()); | ||||
| context.extensions.initialise(); | context.extensions.initialise(); | ||||
| nativeContext->setSwapInterval (1); | |||||
| if (context.renderer != nullptr) | if (context.renderer != nullptr) | ||||
| context.renderer->newOpenGLContextCreated(); | context.renderer->newOpenGLContextCreated(); | ||||
| @@ -353,7 +367,7 @@ public: | |||||
| ReferenceCountedArray<ReferenceCountedObject> associatedObjects; | ReferenceCountedArray<ReferenceCountedObject> associatedObjects; | ||||
| WaitableEvent canPaintNowFlag, finishedPaintingFlag; | WaitableEvent canPaintNowFlag, finishedPaintingFlag; | ||||
| bool shadersAvailable; | |||||
| bool shadersAvailable, hasInitialised; | |||||
| Atomic<int> needsUpdate; | Atomic<int> needsUpdate; | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage) | ||||
| @@ -411,7 +425,7 @@ public: | |||||
| && context.nativeContext != nullptr) | && context.nativeContext != nullptr) | ||||
| { | { | ||||
| if (CachedImage* const c = CachedImage::get (comp)) | if (CachedImage* const c = CachedImage::get (comp)) | ||||
| c->updateViewportSize (true); | |||||
| c->handleResize(); | |||||
| context.nativeContext->updateWindowPosition (comp.getTopLevelComponent() | context.nativeContext->updateWindowPosition (comp.getTopLevelComponent() | ||||
| ->getLocalArea (&comp, comp.getLocalBounds())); | ->getLocalArea (&comp, comp.getLocalBounds())); | ||||
| @@ -562,22 +576,37 @@ Component* OpenGLContext::getTargetComponent() const noexcept | |||||
| return attachment != nullptr ? attachment->getComponent() : nullptr; | return attachment != nullptr ? attachment->getComponent() : nullptr; | ||||
| } | } | ||||
| static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext; | |||||
| OpenGLContext* OpenGLContext::getCurrentContext() | 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 <CachedImage*> (Thread::getCurrentThread())) | |||||
| #endif | |||||
| return ¤tContext->context; | |||||
| return currentThreadActiveContext.get(); | |||||
| } | |||||
| return nullptr; | |||||
| bool OpenGLContext::makeActive() const noexcept | |||||
| { | |||||
| OpenGLContext*& current = currentThreadActiveContext.get(); | |||||
| if (nativeContext != nullptr && nativeContext->makeActive()) | |||||
| { | |||||
| current = const_cast <OpenGLContext*> (this); | |||||
| return true; | |||||
| } | |||||
| current = nullptr; | |||||
| return false; | |||||
| } | } | ||||
| 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::isActive() const noexcept | |||||
| { | |||||
| return nativeContext != nullptr && nativeContext->isActive(); | |||||
| } | |||||
| void OpenGLContext::deactivateCurrentContext() | |||||
| { | |||||
| NativeContext::deactivateCurrentContext(); | |||||
| currentThreadActiveContext.get() = nullptr; | |||||
| } | |||||
| void OpenGLContext::triggerRepaint() | void OpenGLContext::triggerRepaint() | ||||
| { | { | ||||
| @@ -26,16 +26,11 @@ | |||||
| class OpenGLFrameBuffer::Pimpl | class OpenGLFrameBuffer::Pimpl | ||||
| { | { | ||||
| public: | 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) | 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 | // 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. | // context. You'll need to create this object in one of the OpenGLContext's callbacks. | ||||