The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1325 lines
40KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. #if JUCE_IOS
  21. struct AppInactivityCallback // NB: this is a duplicate of an internal declaration in juce_core
  22. {
  23. virtual ~AppInactivityCallback() {}
  24. virtual void appBecomingInactive() = 0;
  25. };
  26. extern Array<AppInactivityCallback*> appBecomingInactiveCallbacks;
  27. // On iOS, all GL calls will crash when the app is running in the background, so
  28. // this prevents them from happening (which some messy locking behaviour)
  29. struct iOSBackgroundProcessCheck : public AppInactivityCallback
  30. {
  31. iOSBackgroundProcessCheck() { isBackgroundProcess(); appBecomingInactiveCallbacks.add (this); }
  32. ~iOSBackgroundProcessCheck() override { appBecomingInactiveCallbacks.removeAllInstancesOf (this); }
  33. bool isBackgroundProcess()
  34. {
  35. const bool b = Process::isForegroundProcess();
  36. isForeground.set (b ? 1 : 0);
  37. return ! b;
  38. }
  39. void appBecomingInactive() override
  40. {
  41. int counter = 2000;
  42. while (--counter > 0 && isForeground.get() != 0)
  43. Thread::sleep (1);
  44. }
  45. private:
  46. Atomic<int> isForeground;
  47. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSBackgroundProcessCheck)
  48. };
  49. #endif
  50. #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
  51. extern JUCE_API double getScaleFactorForWindow (HWND);
  52. #endif
  53. //==============================================================================
  54. class OpenGLContext::CachedImage : public CachedComponentImage,
  55. private ThreadPoolJob
  56. {
  57. public:
  58. CachedImage (OpenGLContext& c, Component& comp,
  59. const OpenGLPixelFormat& pixFormat, void* contextToShare)
  60. : ThreadPoolJob ("OpenGL Rendering"),
  61. context (c), component (comp)
  62. {
  63. nativeContext.reset (new NativeContext (component, pixFormat, contextToShare,
  64. c.useMultisampling, c.versionRequired));
  65. if (nativeContext->createdOk())
  66. context.nativeContext = nativeContext.get();
  67. else
  68. nativeContext.reset();
  69. }
  70. ~CachedImage() override
  71. {
  72. stop();
  73. }
  74. //==============================================================================
  75. void start()
  76. {
  77. if (nativeContext != nullptr)
  78. {
  79. renderThread.reset (new ThreadPool (1));
  80. resume();
  81. }
  82. }
  83. void stop()
  84. {
  85. if (renderThread != nullptr)
  86. {
  87. // make sure everything has finished executing
  88. destroying = true;
  89. if (workQueue.size() > 0)
  90. {
  91. if (! renderThread->contains (this))
  92. resume();
  93. while (workQueue.size() != 0)
  94. Thread::sleep (20);
  95. }
  96. pause();
  97. renderThread.reset();
  98. }
  99. hasInitialised = false;
  100. }
  101. //==============================================================================
  102. void pause()
  103. {
  104. signalJobShouldExit();
  105. messageManagerLock.abort();
  106. if (renderThread != nullptr)
  107. {
  108. repaintEvent.signal();
  109. renderThread->removeJob (this, true, -1);
  110. }
  111. }
  112. void resume()
  113. {
  114. if (renderThread != nullptr)
  115. renderThread->addJob (this, false);
  116. }
  117. //==============================================================================
  118. void paint (Graphics&) override
  119. {
  120. updateViewportSize (false);
  121. }
  122. bool invalidateAll() override
  123. {
  124. validArea.clear();
  125. triggerRepaint();
  126. return false;
  127. }
  128. bool invalidate (const Rectangle<int>& area) override
  129. {
  130. validArea.subtract (area.toFloat().transformedBy (transform).getSmallestIntegerContainer());
  131. triggerRepaint();
  132. return false;
  133. }
  134. void releaseResources() override
  135. {
  136. stop();
  137. }
  138. void triggerRepaint()
  139. {
  140. needsUpdate = 1;
  141. repaintEvent.signal();
  142. }
  143. //==============================================================================
  144. bool ensureFrameBufferSize()
  145. {
  146. auto fbW = cachedImageFrameBuffer.getWidth();
  147. auto fbH = cachedImageFrameBuffer.getHeight();
  148. if (fbW != viewportArea.getWidth() || fbH != viewportArea.getHeight() || ! cachedImageFrameBuffer.isValid())
  149. {
  150. if (! cachedImageFrameBuffer.initialise (context, viewportArea.getWidth(), viewportArea.getHeight()))
  151. return false;
  152. validArea.clear();
  153. JUCE_CHECK_OPENGL_ERROR
  154. }
  155. return true;
  156. }
  157. void clearRegionInFrameBuffer (const RectangleList<int>& list)
  158. {
  159. glClearColor (0, 0, 0, 0);
  160. glEnable (GL_SCISSOR_TEST);
  161. auto previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget();
  162. cachedImageFrameBuffer.makeCurrentRenderingTarget();
  163. auto imageH = cachedImageFrameBuffer.getHeight();
  164. for (auto& r : list)
  165. {
  166. glScissor (r.getX(), imageH - r.getBottom(), r.getWidth(), r.getHeight());
  167. glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  168. }
  169. glDisable (GL_SCISSOR_TEST);
  170. context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget);
  171. JUCE_CHECK_OPENGL_ERROR
  172. }
  173. bool renderFrame()
  174. {
  175. MessageManager::Lock::ScopedTryLockType mmLock (messageManagerLock, false);
  176. auto isUpdatingTestValue = true;
  177. auto isUpdating = needsUpdate.compare_exchange_strong (isUpdatingTestValue, false);
  178. if (context.renderComponents && isUpdating)
  179. {
  180. // This avoids hogging the message thread when doing intensive rendering.
  181. if (lastMMLockReleaseTime + 1 >= Time::getMillisecondCounter())
  182. Thread::sleep (2);
  183. while (! shouldExit())
  184. {
  185. doWorkWhileWaitingForLock (false);
  186. if (mmLock.retryLock())
  187. break;
  188. }
  189. if (shouldExit())
  190. return false;
  191. }
  192. if (! context.makeActive())
  193. return false;
  194. NativeContext::Locker locker (*nativeContext);
  195. JUCE_CHECK_OPENGL_ERROR
  196. doWorkWhileWaitingForLock (true);
  197. if (context.renderer != nullptr)
  198. {
  199. glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
  200. context.currentRenderScale = scale;
  201. context.renderer->renderOpenGL();
  202. clearGLError();
  203. bindVertexArray();
  204. }
  205. if (context.renderComponents)
  206. {
  207. if (isUpdating)
  208. {
  209. paintComponent();
  210. if (! hasInitialised)
  211. return false;
  212. messageManagerLock.exit();
  213. lastMMLockReleaseTime = Time::getMillisecondCounter();
  214. }
  215. glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
  216. drawComponentBuffer();
  217. }
  218. context.swapBuffers();
  219. OpenGLContext::deactivateCurrentContext();
  220. return true;
  221. }
  222. void updateViewportSize (bool canTriggerUpdate)
  223. {
  224. if (auto* peer = component.getPeer())
  225. {
  226. auto localBounds = component.getLocalBounds();
  227. auto displayScale = Desktop::getInstance().getDisplays().getDisplayForRect (component.getTopLevelComponent()->getScreenBounds())->scale;
  228. auto newArea = peer->getComponent().getLocalArea (&component, localBounds).withZeroOrigin() * displayScale;
  229. #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
  230. auto newScale = getScaleFactorForWindow (nativeContext->getNativeHandle());
  231. auto desktopScale = Desktop::getInstance().getGlobalScaleFactor();
  232. if (! approximatelyEqual (1.0f, desktopScale))
  233. newScale *= desktopScale;
  234. #else
  235. auto newScale = displayScale;
  236. #endif
  237. if (scale != newScale || viewportArea != newArea)
  238. {
  239. scale = newScale;
  240. viewportArea = newArea;
  241. transform = AffineTransform::scale ((float) newArea.getWidth() / (float) localBounds.getWidth(),
  242. (float) newArea.getHeight() / (float) localBounds.getHeight());
  243. nativeContext->updateWindowPosition (peer->getAreaCoveredBy (component));
  244. if (canTriggerUpdate)
  245. invalidateAll();
  246. }
  247. }
  248. }
  249. void bindVertexArray() noexcept
  250. {
  251. if (vertexArrayObject != 0)
  252. context.extensions.glBindVertexArray (vertexArrayObject);
  253. }
  254. void checkViewportBounds()
  255. {
  256. auto screenBounds = component.getTopLevelComponent()->getScreenBounds();
  257. if (lastScreenBounds != screenBounds)
  258. updateViewportSize (true);
  259. }
  260. void paintComponent()
  261. {
  262. // you mustn't set your own cached image object when attaching a GL context!
  263. jassert (get (component) == this);
  264. if (! ensureFrameBufferSize())
  265. return;
  266. RectangleList<int> invalid (viewportArea);
  267. invalid.subtract (validArea);
  268. validArea = viewportArea;
  269. if (! invalid.isEmpty())
  270. {
  271. clearRegionInFrameBuffer (invalid);
  272. {
  273. std::unique_ptr<LowLevelGraphicsContext> g (createOpenGLGraphicsContext (context, cachedImageFrameBuffer));
  274. g->clipToRectangleList (invalid);
  275. g->addTransform (transform);
  276. paintOwner (*g);
  277. JUCE_CHECK_OPENGL_ERROR
  278. }
  279. if (! context.isActive())
  280. context.makeActive();
  281. }
  282. JUCE_CHECK_OPENGL_ERROR
  283. }
  284. void drawComponentBuffer()
  285. {
  286. #if ! JUCE_ANDROID
  287. glEnable (GL_TEXTURE_2D);
  288. clearGLError();
  289. #endif
  290. #if JUCE_WINDOWS
  291. // some stupidly old drivers are missing this function, so try to at least avoid a crash here,
  292. // but if you hit this assertion you may want to have your own version check before using the
  293. // component rendering stuff on such old drivers.
  294. jassert (context.extensions.glActiveTexture != nullptr);
  295. if (context.extensions.glActiveTexture != nullptr)
  296. #endif
  297. context.extensions.glActiveTexture (GL_TEXTURE0);
  298. glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID());
  299. bindVertexArray();
  300. const Rectangle<int> cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight());
  301. context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight(), false);
  302. glBindTexture (GL_TEXTURE_2D, 0);
  303. JUCE_CHECK_OPENGL_ERROR
  304. }
  305. void paintOwner (LowLevelGraphicsContext& llgc)
  306. {
  307. Graphics g (llgc);
  308. #if JUCE_ENABLE_REPAINT_DEBUGGING
  309. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  310. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  311. #endif
  312. {
  313. g.saveState();
  314. }
  315. #endif
  316. JUCE_TRY
  317. {
  318. component.paintEntireComponent (g, false);
  319. }
  320. JUCE_CATCH_EXCEPTION
  321. #if JUCE_ENABLE_REPAINT_DEBUGGING
  322. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  323. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  324. #endif
  325. {
  326. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  327. // clearly when things are being repainted.
  328. g.restoreState();
  329. static Random rng;
  330. g.fillAll (Colour ((uint8) rng.nextInt (255),
  331. (uint8) rng.nextInt (255),
  332. (uint8) rng.nextInt (255),
  333. (uint8) 0x50));
  334. }
  335. #endif
  336. }
  337. void handleResize()
  338. {
  339. updateViewportSize (true);
  340. #if JUCE_MAC
  341. if (hasInitialised)
  342. {
  343. [nativeContext->view update];
  344. renderFrame();
  345. }
  346. #endif
  347. }
  348. //==============================================================================
  349. JobStatus runJob() override
  350. {
  351. {
  352. // Allow the message thread to finish setting-up the context before using it..
  353. MessageManager::Lock::ScopedTryLockType mmLock (messageManagerLock, false);
  354. do
  355. {
  356. if (shouldExit())
  357. return ThreadPoolJob::jobHasFinished;
  358. } while (! mmLock.retryLock());
  359. }
  360. if (! initialiseOnThread())
  361. {
  362. hasInitialised = false;
  363. return ThreadPoolJob::jobHasFinished;
  364. }
  365. hasInitialised = true;
  366. while (! shouldExit())
  367. {
  368. #if JUCE_IOS
  369. if (backgroundProcessCheck.isBackgroundProcess())
  370. {
  371. repaintEvent.wait (300);
  372. continue;
  373. }
  374. #endif
  375. if (shouldExit())
  376. break;
  377. #if JUCE_MAC
  378. if (cvDisplayLinkWrapper != nullptr)
  379. {
  380. repaintEvent.wait (-1);
  381. renderFrame();
  382. repaintEvent.reset();
  383. }
  384. else
  385. #endif
  386. if (! renderFrame())
  387. repaintEvent.wait (5); // failed to render, so avoid a tight fail-loop.
  388. else if (! context.continuousRepaint && ! shouldExit())
  389. repaintEvent.wait (-1);
  390. }
  391. hasInitialised = false;
  392. context.makeActive();
  393. shutdownOnThread();
  394. OpenGLContext::deactivateCurrentContext();
  395. return ThreadPoolJob::jobHasFinished;
  396. }
  397. bool initialiseOnThread()
  398. {
  399. // On android, this can get called twice, so drop any previous state..
  400. associatedObjectNames.clear();
  401. associatedObjects.clear();
  402. cachedImageFrameBuffer.release();
  403. context.makeActive();
  404. if (! nativeContext->initialiseOnRenderThread (context))
  405. return false;
  406. #if JUCE_ANDROID
  407. // On android the context may be created in initialiseOnRenderThread
  408. // and we therefore need to call makeActive again
  409. context.makeActive();
  410. #endif
  411. gl::loadFunctions();
  412. if (OpenGLShaderProgram::getLanguageVersion() > 1.2)
  413. {
  414. context.extensions.glGenVertexArrays (1, &vertexArrayObject);
  415. bindVertexArray();
  416. }
  417. glViewport (0, 0, component.getWidth(), component.getHeight());
  418. nativeContext->setSwapInterval (1);
  419. JUCE_CHECK_OPENGL_ERROR
  420. shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0;
  421. clearGLError();
  422. if (context.renderer != nullptr)
  423. context.renderer->newOpenGLContextCreated();
  424. #if JUCE_MAC
  425. if (context.continuousRepaint)
  426. cvDisplayLinkWrapper = std::make_unique<CVDisplayLinkWrapper> (this);
  427. #endif
  428. return true;
  429. }
  430. void shutdownOnThread()
  431. {
  432. #if JUCE_MAC
  433. cvDisplayLinkWrapper = nullptr;
  434. #endif
  435. if (context.renderer != nullptr)
  436. context.renderer->openGLContextClosing();
  437. if (vertexArrayObject != 0)
  438. context.extensions.glDeleteVertexArrays (1, &vertexArrayObject);
  439. associatedObjectNames.clear();
  440. associatedObjects.clear();
  441. cachedImageFrameBuffer.release();
  442. nativeContext->shutdownOnRenderThread();
  443. }
  444. //==============================================================================
  445. struct BlockingWorker : public OpenGLContext::AsyncWorker
  446. {
  447. BlockingWorker (OpenGLContext::AsyncWorker::Ptr && workerToUse)
  448. : originalWorker (std::move (workerToUse))
  449. {}
  450. void operator() (OpenGLContext& calleeContext)
  451. {
  452. if (originalWorker != nullptr)
  453. (*originalWorker) (calleeContext);
  454. finishedSignal.signal();
  455. }
  456. void block() noexcept { finishedSignal.wait(); }
  457. OpenGLContext::AsyncWorker::Ptr originalWorker;
  458. WaitableEvent finishedSignal;
  459. };
  460. bool doWorkWhileWaitingForLock (bool contextIsAlreadyActive)
  461. {
  462. bool contextActivated = false;
  463. for (OpenGLContext::AsyncWorker::Ptr work = workQueue.removeAndReturn (0);
  464. work != nullptr && (! shouldExit()); work = workQueue.removeAndReturn (0))
  465. {
  466. if ((! contextActivated) && (! contextIsAlreadyActive))
  467. {
  468. if (! context.makeActive())
  469. break;
  470. contextActivated = true;
  471. }
  472. NativeContext::Locker locker (*nativeContext);
  473. (*work) (context);
  474. clearGLError();
  475. }
  476. if (contextActivated)
  477. OpenGLContext::deactivateCurrentContext();
  478. return shouldExit();
  479. }
  480. void execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock, bool calledFromDestructor = false)
  481. {
  482. if (calledFromDestructor || ! destroying)
  483. {
  484. if (shouldBlock)
  485. {
  486. auto blocker = new BlockingWorker (std::move (workerToUse));
  487. OpenGLContext::AsyncWorker::Ptr worker (*blocker);
  488. workQueue.add (worker);
  489. messageManagerLock.abort();
  490. context.triggerRepaint();
  491. blocker->block();
  492. }
  493. else
  494. {
  495. workQueue.add (std::move (workerToUse));
  496. messageManagerLock.abort();
  497. context.triggerRepaint();
  498. }
  499. }
  500. else
  501. {
  502. jassertfalse; // you called execute AFTER you detached your openglcontext
  503. }
  504. }
  505. //==============================================================================
  506. static CachedImage* get (Component& c) noexcept
  507. {
  508. return dynamic_cast<CachedImage*> (c.getCachedComponentImage());
  509. }
  510. //==============================================================================
  511. friend class NativeContext;
  512. std::unique_ptr<NativeContext> nativeContext;
  513. OpenGLContext& context;
  514. Component& component;
  515. OpenGLFrameBuffer cachedImageFrameBuffer;
  516. RectangleList<int> validArea;
  517. Rectangle<int> viewportArea, lastScreenBounds;
  518. double scale = 1.0;
  519. AffineTransform transform;
  520. GLuint vertexArrayObject = 0;
  521. StringArray associatedObjectNames;
  522. ReferenceCountedArray<ReferenceCountedObject> associatedObjects;
  523. WaitableEvent canPaintNowFlag, finishedPaintingFlag, repaintEvent { true };
  524. #if JUCE_OPENGL_ES
  525. bool shadersAvailable = true;
  526. #else
  527. bool shadersAvailable = false;
  528. #endif
  529. std::atomic<bool> hasInitialised { false }, needsUpdate { true }, destroying { false };
  530. uint32 lastMMLockReleaseTime = 0;
  531. #if JUCE_MAC
  532. struct CVDisplayLinkWrapper
  533. {
  534. CVDisplayLinkWrapper (CachedImage* im)
  535. {
  536. CVDisplayLinkCreateWithActiveCGDisplays (&displayLink);
  537. CVDisplayLinkSetOutputCallback (displayLink, &displayLinkCallback, im);
  538. CVDisplayLinkStart (displayLink);
  539. }
  540. ~CVDisplayLinkWrapper()
  541. {
  542. CVDisplayLinkStop (displayLink);
  543. CVDisplayLinkRelease (displayLink);
  544. }
  545. static CVReturn displayLinkCallback (CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*,
  546. CVOptionFlags, CVOptionFlags*, void* displayLinkContext)
  547. {
  548. auto* self = (CachedImage*) displayLinkContext;
  549. self->repaintEvent.signal();
  550. return kCVReturnSuccess;
  551. }
  552. CVDisplayLinkRef displayLink;
  553. };
  554. std::unique_ptr<CVDisplayLinkWrapper> cvDisplayLinkWrapper;
  555. #endif
  556. std::unique_ptr<ThreadPool> renderThread;
  557. ReferenceCountedArray<OpenGLContext::AsyncWorker, CriticalSection> workQueue;
  558. MessageManager::Lock messageManagerLock;
  559. #if JUCE_IOS
  560. iOSBackgroundProcessCheck backgroundProcessCheck;
  561. #endif
  562. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
  563. };
  564. //==============================================================================
  565. class OpenGLContext::Attachment : public ComponentMovementWatcher,
  566. private Timer
  567. {
  568. public:
  569. Attachment (OpenGLContext& c, Component& comp)
  570. : ComponentMovementWatcher (&comp), context (c)
  571. {
  572. if (canBeAttached (comp))
  573. attach();
  574. }
  575. ~Attachment() override
  576. {
  577. detach();
  578. }
  579. void detach()
  580. {
  581. auto& comp = *getComponent();
  582. stop();
  583. comp.setCachedComponentImage (nullptr);
  584. context.nativeContext = nullptr;
  585. }
  586. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  587. {
  588. auto& comp = *getComponent();
  589. if (isAttached (comp) != canBeAttached (comp))
  590. componentVisibilityChanged();
  591. if (comp.getWidth() > 0 && comp.getHeight() > 0
  592. && context.nativeContext != nullptr)
  593. {
  594. if (auto* c = CachedImage::get (comp))
  595. c->handleResize();
  596. if (auto* peer = comp.getTopLevelComponent()->getPeer())
  597. context.nativeContext->updateWindowPosition (peer->getAreaCoveredBy (comp));
  598. }
  599. }
  600. using ComponentMovementWatcher::componentMovedOrResized;
  601. void componentPeerChanged() override
  602. {
  603. detach();
  604. componentVisibilityChanged();
  605. }
  606. void componentVisibilityChanged() override
  607. {
  608. auto& comp = *getComponent();
  609. if (canBeAttached (comp))
  610. {
  611. if (isAttached (comp))
  612. comp.repaint(); // (needed when windows are un-minimised)
  613. else
  614. attach();
  615. }
  616. else
  617. {
  618. detach();
  619. }
  620. }
  621. using ComponentMovementWatcher::componentVisibilityChanged;
  622. #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
  623. void componentBeingDeleted (Component& c) override
  624. {
  625. /* You must call detach() or delete your OpenGLContext to remove it
  626. from a component BEFORE deleting the component that it is using!
  627. */
  628. jassertfalse;
  629. ComponentMovementWatcher::componentBeingDeleted (c);
  630. }
  631. #endif
  632. void update()
  633. {
  634. auto& comp = *getComponent();
  635. if (canBeAttached (comp))
  636. start();
  637. else
  638. stop();
  639. }
  640. private:
  641. OpenGLContext& context;
  642. bool canBeAttached (const Component& comp) noexcept
  643. {
  644. return (! context.overrideCanAttach) && comp.getWidth() > 0 && comp.getHeight() > 0 && isShowingOrMinimised (comp);
  645. }
  646. static bool isShowingOrMinimised (const Component& c)
  647. {
  648. if (! c.isVisible())
  649. return false;
  650. if (auto* p = c.getParentComponent())
  651. return isShowingOrMinimised (*p);
  652. return c.getPeer() != nullptr;
  653. }
  654. static bool isAttached (const Component& comp) noexcept
  655. {
  656. return comp.getCachedComponentImage() != nullptr;
  657. }
  658. void attach()
  659. {
  660. auto& comp = *getComponent();
  661. auto* newCachedImage = new CachedImage (context, comp,
  662. context.openGLPixelFormat,
  663. context.contextToShareWith);
  664. comp.setCachedComponentImage (newCachedImage);
  665. start();
  666. }
  667. void stop()
  668. {
  669. stopTimer();
  670. auto& comp = *getComponent();
  671. #if JUCE_MAC
  672. [[(NSView*) comp.getWindowHandle() window] disableScreenUpdatesUntilFlush];
  673. #endif
  674. if (auto* oldCachedImage = CachedImage::get (comp))
  675. oldCachedImage->stop(); // (must stop this before detaching it from the component)
  676. }
  677. void start()
  678. {
  679. auto& comp = *getComponent();
  680. if (auto* cachedImage = CachedImage::get (comp))
  681. {
  682. cachedImage->start(); // (must wait until this is attached before starting its thread)
  683. cachedImage->updateViewportSize (true);
  684. startTimer (400);
  685. }
  686. }
  687. void timerCallback() override
  688. {
  689. if (auto* cachedImage = CachedImage::get (*getComponent()))
  690. cachedImage->checkViewportBounds();
  691. }
  692. };
  693. //==============================================================================
  694. OpenGLContext::OpenGLContext()
  695. {
  696. }
  697. OpenGLContext::~OpenGLContext()
  698. {
  699. detach();
  700. }
  701. void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept
  702. {
  703. // This method must not be called when the context has already been attached!
  704. // Call it before attaching your context, or use detach() first, before calling this!
  705. jassert (nativeContext == nullptr);
  706. renderer = rendererToUse;
  707. }
  708. void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept
  709. {
  710. // This method must not be called when the context has already been attached!
  711. // Call it before attaching your context, or use detach() first, before calling this!
  712. jassert (nativeContext == nullptr);
  713. renderComponents = shouldPaintComponent;
  714. }
  715. void OpenGLContext::setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept
  716. {
  717. continuousRepaint = shouldContinuouslyRepaint;
  718. #if JUCE_MAC
  719. if (auto* component = getTargetComponent())
  720. {
  721. detach();
  722. attachment.reset (new Attachment (*this, *component));
  723. }
  724. #endif
  725. triggerRepaint();
  726. }
  727. void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept
  728. {
  729. // This method must not be called when the context has already been attached!
  730. // Call it before attaching your context, or use detach() first, before calling this!
  731. jassert (nativeContext == nullptr);
  732. openGLPixelFormat = preferredPixelFormat;
  733. }
  734. void OpenGLContext::setTextureMagnificationFilter (OpenGLContext::TextureMagnificationFilter magFilterMode) noexcept
  735. {
  736. texMagFilter = magFilterMode;
  737. }
  738. void OpenGLContext::setNativeSharedContext (void* nativeContextToShareWith) noexcept
  739. {
  740. // This method must not be called when the context has already been attached!
  741. // Call it before attaching your context, or use detach() first, before calling this!
  742. jassert (nativeContext == nullptr);
  743. contextToShareWith = nativeContextToShareWith;
  744. }
  745. void OpenGLContext::setMultisamplingEnabled (bool b) noexcept
  746. {
  747. // This method must not be called when the context has already been attached!
  748. // Call it before attaching your context, or use detach() first, before calling this!
  749. jassert (nativeContext == nullptr);
  750. useMultisampling = b;
  751. }
  752. void OpenGLContext::setOpenGLVersionRequired (OpenGLVersion v) noexcept
  753. {
  754. versionRequired = v;
  755. }
  756. void OpenGLContext::attachTo (Component& component)
  757. {
  758. component.repaint();
  759. if (getTargetComponent() != &component)
  760. {
  761. detach();
  762. attachment.reset (new Attachment (*this, component));
  763. }
  764. }
  765. void OpenGLContext::detach()
  766. {
  767. if (auto* a = attachment.get())
  768. {
  769. a->detach(); // must detach before nulling our pointer
  770. attachment.reset();
  771. }
  772. nativeContext = nullptr;
  773. }
  774. bool OpenGLContext::isAttached() const noexcept
  775. {
  776. return nativeContext != nullptr;
  777. }
  778. Component* OpenGLContext::getTargetComponent() const noexcept
  779. {
  780. return attachment != nullptr ? attachment->getComponent() : nullptr;
  781. }
  782. OpenGLContext* OpenGLContext::getContextAttachedTo (Component& c) noexcept
  783. {
  784. if (auto* ci = CachedImage::get (c))
  785. return &(ci->context);
  786. return nullptr;
  787. }
  788. static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext;
  789. OpenGLContext* OpenGLContext::getCurrentContext()
  790. {
  791. return currentThreadActiveContext.get();
  792. }
  793. bool OpenGLContext::makeActive() const noexcept
  794. {
  795. auto& current = currentThreadActiveContext.get();
  796. if (nativeContext != nullptr && nativeContext->makeActive())
  797. {
  798. current = const_cast<OpenGLContext*> (this);
  799. return true;
  800. }
  801. current = nullptr;
  802. return false;
  803. }
  804. bool OpenGLContext::isActive() const noexcept
  805. {
  806. return nativeContext != nullptr && nativeContext->isActive();
  807. }
  808. void OpenGLContext::deactivateCurrentContext()
  809. {
  810. NativeContext::deactivateCurrentContext();
  811. currentThreadActiveContext.get() = nullptr;
  812. }
  813. void OpenGLContext::triggerRepaint()
  814. {
  815. if (auto* cachedImage = getCachedImage())
  816. cachedImage->triggerRepaint();
  817. }
  818. void OpenGLContext::swapBuffers()
  819. {
  820. if (nativeContext != nullptr)
  821. nativeContext->swapBuffers();
  822. }
  823. unsigned int OpenGLContext::getFrameBufferID() const noexcept
  824. {
  825. return nativeContext != nullptr ? nativeContext->getFrameBufferID() : 0;
  826. }
  827. bool OpenGLContext::setSwapInterval (int numFramesPerSwap)
  828. {
  829. return nativeContext != nullptr && nativeContext->setSwapInterval (numFramesPerSwap);
  830. }
  831. int OpenGLContext::getSwapInterval() const
  832. {
  833. return nativeContext != nullptr ? nativeContext->getSwapInterval() : 0;
  834. }
  835. void* OpenGLContext::getRawContext() const noexcept
  836. {
  837. return nativeContext != nullptr ? nativeContext->getRawContext() : nullptr;
  838. }
  839. OpenGLContext::CachedImage* OpenGLContext::getCachedImage() const noexcept
  840. {
  841. if (auto* comp = getTargetComponent())
  842. return CachedImage::get (*comp);
  843. return nullptr;
  844. }
  845. bool OpenGLContext::areShadersAvailable() const
  846. {
  847. auto* c = getCachedImage();
  848. return c != nullptr && c->shadersAvailable;
  849. }
  850. ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const
  851. {
  852. jassert (name != nullptr);
  853. auto* c = getCachedImage();
  854. // This method must only be called from an openGL rendering callback.
  855. jassert (c != nullptr && nativeContext != nullptr);
  856. jassert (getCurrentContext() != nullptr);
  857. auto index = c->associatedObjectNames.indexOf (name);
  858. return index >= 0 ? c->associatedObjects.getUnchecked (index).get() : nullptr;
  859. }
  860. void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObject* newObject)
  861. {
  862. jassert (name != nullptr);
  863. if (auto* c = getCachedImage())
  864. {
  865. // This method must only be called from an openGL rendering callback.
  866. jassert (nativeContext != nullptr);
  867. jassert (getCurrentContext() != nullptr);
  868. const int index = c->associatedObjectNames.indexOf (name);
  869. if (index >= 0)
  870. {
  871. if (newObject != nullptr)
  872. {
  873. c->associatedObjects.set (index, newObject);
  874. }
  875. else
  876. {
  877. c->associatedObjectNames.remove (index);
  878. c->associatedObjects.remove (index);
  879. }
  880. }
  881. else if (newObject != nullptr)
  882. {
  883. c->associatedObjectNames.add (name);
  884. c->associatedObjects.add (newObject);
  885. }
  886. }
  887. }
  888. void OpenGLContext::setImageCacheSize (size_t newSize) noexcept { imageCacheMaxSize = newSize; }
  889. size_t OpenGLContext::getImageCacheSize() const noexcept { return imageCacheMaxSize; }
  890. void OpenGLContext::execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock)
  891. {
  892. if (auto* c = getCachedImage())
  893. c->execute (std::move (workerToUse), shouldBlock);
  894. else
  895. jassertfalse; // You must have attached the context to a component
  896. }
  897. //==============================================================================
  898. struct DepthTestDisabler
  899. {
  900. DepthTestDisabler() noexcept
  901. {
  902. glGetBooleanv (GL_DEPTH_TEST, &wasEnabled);
  903. if (wasEnabled)
  904. glDisable (GL_DEPTH_TEST);
  905. }
  906. ~DepthTestDisabler() noexcept
  907. {
  908. if (wasEnabled)
  909. glEnable (GL_DEPTH_TEST);
  910. }
  911. GLboolean wasEnabled;
  912. };
  913. //==============================================================================
  914. void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
  915. const Rectangle<int>& anchorPosAndTextureSize,
  916. const int contextWidth, const int contextHeight,
  917. bool flippedVertically)
  918. {
  919. if (contextWidth <= 0 || contextHeight <= 0)
  920. return;
  921. JUCE_CHECK_OPENGL_ERROR
  922. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  923. glEnable (GL_BLEND);
  924. DepthTestDisabler depthDisabler;
  925. if (areShadersAvailable())
  926. {
  927. struct OverlayShaderProgram : public ReferenceCountedObject
  928. {
  929. OverlayShaderProgram (OpenGLContext& context)
  930. : program (context), builder (program), params (program)
  931. {}
  932. static const OverlayShaderProgram& select (OpenGLContext& context)
  933. {
  934. static const char programValueID[] = "juceGLComponentOverlayShader";
  935. OverlayShaderProgram* program = static_cast<OverlayShaderProgram*> (context.getAssociatedObject (programValueID));
  936. if (program == nullptr)
  937. {
  938. program = new OverlayShaderProgram (context);
  939. context.setAssociatedObject (programValueID, program);
  940. }
  941. program->program.use();
  942. return *program;
  943. }
  944. struct ProgramBuilder
  945. {
  946. ProgramBuilder (OpenGLShaderProgram& prog)
  947. {
  948. prog.addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (
  949. "attribute " JUCE_HIGHP " vec2 position;"
  950. "uniform " JUCE_HIGHP " vec2 screenSize;"
  951. "uniform " JUCE_HIGHP " float textureBounds[4];"
  952. "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
  953. "varying " JUCE_HIGHP " vec2 texturePos;"
  954. "void main()"
  955. "{"
  956. JUCE_HIGHP " vec2 scaled = position / (0.5 * screenSize.xy);"
  957. "gl_Position = vec4 (scaled.x - 1.0, 1.0 - scaled.y, 0, 1.0);"
  958. "texturePos = (position - vec2 (textureBounds[0], textureBounds[1])) / vec2 (textureBounds[2], textureBounds[3]);"
  959. "texturePos = vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y);"
  960. "}"));
  961. prog.addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (
  962. "uniform sampler2D imageTexture;"
  963. "varying " JUCE_HIGHP " vec2 texturePos;"
  964. "void main()"
  965. "{"
  966. "gl_FragColor = texture2D (imageTexture, texturePos);"
  967. "}"));
  968. prog.link();
  969. }
  970. };
  971. struct Params
  972. {
  973. Params (OpenGLShaderProgram& prog)
  974. : positionAttribute (prog, "position"),
  975. screenSize (prog, "screenSize"),
  976. imageTexture (prog, "imageTexture"),
  977. textureBounds (prog, "textureBounds"),
  978. vOffsetAndScale (prog, "vOffsetAndScale")
  979. {}
  980. void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flipVertically) const
  981. {
  982. const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
  983. textureBounds.set (m, 4);
  984. imageTexture.set (0);
  985. screenSize.set (targetWidth, targetHeight);
  986. vOffsetAndScale.set (flipVertically ? 0.0f : 1.0f,
  987. flipVertically ? 1.0f : -1.0f);
  988. }
  989. OpenGLShaderProgram::Attribute positionAttribute;
  990. OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
  991. };
  992. OpenGLShaderProgram program;
  993. ProgramBuilder builder;
  994. Params params;
  995. };
  996. auto left = (GLshort) targetClipArea.getX();
  997. auto top = (GLshort) targetClipArea.getY();
  998. auto right = (GLshort) targetClipArea.getRight();
  999. auto bottom = (GLshort) targetClipArea.getBottom();
  1000. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  1001. auto& program = OverlayShaderProgram::select (*this);
  1002. program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
  1003. GLuint vertexBuffer = 0;
  1004. extensions.glGenBuffers (1, &vertexBuffer);
  1005. extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  1006. extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
  1007. auto index = (GLuint) program.params.positionAttribute.attributeID;
  1008. extensions.glVertexAttribPointer (index, 2, GL_SHORT, GL_FALSE, 4, nullptr);
  1009. extensions.glEnableVertexAttribArray (index);
  1010. JUCE_CHECK_OPENGL_ERROR
  1011. if (extensions.glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
  1012. {
  1013. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  1014. extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  1015. extensions.glUseProgram (0);
  1016. extensions.glDisableVertexAttribArray (index);
  1017. extensions.glDeleteBuffers (1, &vertexBuffer);
  1018. }
  1019. else
  1020. {
  1021. clearGLError();
  1022. }
  1023. }
  1024. else
  1025. {
  1026. jassert (attachment == nullptr); // Running on an old graphics card!
  1027. }
  1028. JUCE_CHECK_OPENGL_ERROR
  1029. }
  1030. #if JUCE_ANDROID
  1031. EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY;
  1032. EGLDisplay OpenGLContext::NativeContext::config;
  1033. void OpenGLContext::NativeContext::surfaceCreated (LocalRef<jobject> holder)
  1034. {
  1035. ignoreUnused (holder);
  1036. if (auto* cachedImage = CachedImage::get (component))
  1037. {
  1038. if (auto* pool = cachedImage->renderThread.get())
  1039. {
  1040. if (! pool->contains (cachedImage))
  1041. {
  1042. cachedImage->resume();
  1043. cachedImage->context.triggerRepaint();
  1044. }
  1045. }
  1046. }
  1047. }
  1048. void OpenGLContext::NativeContext::surfaceDestroyed (LocalRef<jobject> holder)
  1049. {
  1050. ignoreUnused (holder);
  1051. // unlike the name suggests this will be called just before the
  1052. // surface is destroyed. We need to pause the render thread.
  1053. if (auto* cachedImage = CachedImage::get (component))
  1054. {
  1055. cachedImage->pause();
  1056. if (auto* threadPool = cachedImage->renderThread.get())
  1057. threadPool->waitForJobToFinish (cachedImage, -1);
  1058. }
  1059. }
  1060. #endif
  1061. } // namespace juce