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.

1327 lines
40KB

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