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.

1324 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. }
  383. else
  384. #endif
  385. if (! renderFrame())
  386. repaintEvent.wait (5); // failed to render, so avoid a tight fail-loop.
  387. else if (! context.continuousRepaint && ! shouldExit())
  388. repaintEvent.wait (-1);
  389. }
  390. hasInitialised = false;
  391. context.makeActive();
  392. shutdownOnThread();
  393. OpenGLContext::deactivateCurrentContext();
  394. return ThreadPoolJob::jobHasFinished;
  395. }
  396. bool initialiseOnThread()
  397. {
  398. // On android, this can get called twice, so drop any previous state..
  399. associatedObjectNames.clear();
  400. associatedObjects.clear();
  401. cachedImageFrameBuffer.release();
  402. context.makeActive();
  403. if (! nativeContext->initialiseOnRenderThread (context))
  404. return false;
  405. #if JUCE_ANDROID
  406. // On android the context may be created in initialiseOnRenderThread
  407. // and we therefore need to call makeActive again
  408. context.makeActive();
  409. #endif
  410. gl::loadFunctions();
  411. if (OpenGLShaderProgram::getLanguageVersion() > 1.2)
  412. {
  413. context.extensions.glGenVertexArrays (1, &vertexArrayObject);
  414. bindVertexArray();
  415. }
  416. glViewport (0, 0, component.getWidth(), component.getHeight());
  417. nativeContext->setSwapInterval (1);
  418. JUCE_CHECK_OPENGL_ERROR
  419. shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0;
  420. clearGLError();
  421. if (context.renderer != nullptr)
  422. context.renderer->newOpenGLContextCreated();
  423. #if JUCE_MAC
  424. if (context.continuousRepaint)
  425. cvDisplayLinkWrapper = std::make_unique<CVDisplayLinkWrapper> (this);
  426. #endif
  427. return true;
  428. }
  429. void shutdownOnThread()
  430. {
  431. #if JUCE_MAC
  432. cvDisplayLinkWrapper = nullptr;
  433. #endif
  434. if (context.renderer != nullptr)
  435. context.renderer->openGLContextClosing();
  436. if (vertexArrayObject != 0)
  437. context.extensions.glDeleteVertexArrays (1, &vertexArrayObject);
  438. associatedObjectNames.clear();
  439. associatedObjects.clear();
  440. cachedImageFrameBuffer.release();
  441. nativeContext->shutdownOnRenderThread();
  442. }
  443. //==============================================================================
  444. struct BlockingWorker : public OpenGLContext::AsyncWorker
  445. {
  446. BlockingWorker (OpenGLContext::AsyncWorker::Ptr && workerToUse)
  447. : originalWorker (std::move (workerToUse))
  448. {}
  449. void operator() (OpenGLContext& calleeContext)
  450. {
  451. if (originalWorker != nullptr)
  452. (*originalWorker) (calleeContext);
  453. finishedSignal.signal();
  454. }
  455. void block() noexcept { finishedSignal.wait(); }
  456. OpenGLContext::AsyncWorker::Ptr originalWorker;
  457. WaitableEvent finishedSignal;
  458. };
  459. bool doWorkWhileWaitingForLock (bool contextIsAlreadyActive)
  460. {
  461. bool contextActivated = false;
  462. for (OpenGLContext::AsyncWorker::Ptr work = workQueue.removeAndReturn (0);
  463. work != nullptr && (! shouldExit()); work = workQueue.removeAndReturn (0))
  464. {
  465. if ((! contextActivated) && (! contextIsAlreadyActive))
  466. {
  467. if (! context.makeActive())
  468. break;
  469. contextActivated = true;
  470. }
  471. NativeContext::Locker locker (*nativeContext);
  472. (*work) (context);
  473. clearGLError();
  474. }
  475. if (contextActivated)
  476. OpenGLContext::deactivateCurrentContext();
  477. return shouldExit();
  478. }
  479. void execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock, bool calledFromDestructor = false)
  480. {
  481. if (calledFromDestructor || ! destroying)
  482. {
  483. if (shouldBlock)
  484. {
  485. auto blocker = new BlockingWorker (std::move (workerToUse));
  486. OpenGLContext::AsyncWorker::Ptr worker (*blocker);
  487. workQueue.add (worker);
  488. messageManagerLock.abort();
  489. context.triggerRepaint();
  490. blocker->block();
  491. }
  492. else
  493. {
  494. workQueue.add (std::move (workerToUse));
  495. messageManagerLock.abort();
  496. context.triggerRepaint();
  497. }
  498. }
  499. else
  500. {
  501. jassertfalse; // you called execute AFTER you detached your openglcontext
  502. }
  503. }
  504. //==============================================================================
  505. static CachedImage* get (Component& c) noexcept
  506. {
  507. return dynamic_cast<CachedImage*> (c.getCachedComponentImage());
  508. }
  509. //==============================================================================
  510. friend class NativeContext;
  511. std::unique_ptr<NativeContext> nativeContext;
  512. OpenGLContext& context;
  513. Component& component;
  514. OpenGLFrameBuffer cachedImageFrameBuffer;
  515. RectangleList<int> validArea;
  516. Rectangle<int> viewportArea, lastScreenBounds;
  517. double scale = 1.0;
  518. AffineTransform transform;
  519. GLuint vertexArrayObject = 0;
  520. StringArray associatedObjectNames;
  521. ReferenceCountedArray<ReferenceCountedObject> associatedObjects;
  522. WaitableEvent canPaintNowFlag, finishedPaintingFlag, repaintEvent;
  523. #if JUCE_OPENGL_ES
  524. bool shadersAvailable = true;
  525. #else
  526. bool shadersAvailable = false;
  527. #endif
  528. std::atomic<bool> hasInitialised { false }, needsUpdate { true }, destroying { false };
  529. uint32 lastMMLockReleaseTime = 0;
  530. #if JUCE_MAC
  531. struct CVDisplayLinkWrapper
  532. {
  533. CVDisplayLinkWrapper (CachedImage* im)
  534. {
  535. CVDisplayLinkCreateWithActiveCGDisplays (&displayLink);
  536. CVDisplayLinkSetOutputCallback (displayLink, &displayLinkCallback, im);
  537. CVDisplayLinkStart (displayLink);
  538. }
  539. ~CVDisplayLinkWrapper()
  540. {
  541. CVDisplayLinkStop (displayLink);
  542. CVDisplayLinkRelease (displayLink);
  543. }
  544. static CVReturn displayLinkCallback (CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*,
  545. CVOptionFlags, CVOptionFlags*, void* displayLinkContext)
  546. {
  547. auto* self = (CachedImage*) displayLinkContext;
  548. self->repaintEvent.signal();
  549. return kCVReturnSuccess;
  550. }
  551. CVDisplayLinkRef displayLink;
  552. };
  553. std::unique_ptr<CVDisplayLinkWrapper> cvDisplayLinkWrapper;
  554. #endif
  555. std::unique_ptr<ThreadPool> renderThread;
  556. ReferenceCountedArray<OpenGLContext::AsyncWorker, CriticalSection> workQueue;
  557. MessageManager::Lock messageManagerLock;
  558. #if JUCE_IOS
  559. iOSBackgroundProcessCheck backgroundProcessCheck;
  560. #endif
  561. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
  562. };
  563. //==============================================================================
  564. class OpenGLContext::Attachment : public ComponentMovementWatcher,
  565. private Timer
  566. {
  567. public:
  568. Attachment (OpenGLContext& c, Component& comp)
  569. : ComponentMovementWatcher (&comp), context (c)
  570. {
  571. if (canBeAttached (comp))
  572. attach();
  573. }
  574. ~Attachment() override
  575. {
  576. detach();
  577. }
  578. void detach()
  579. {
  580. auto& comp = *getComponent();
  581. stop();
  582. comp.setCachedComponentImage (nullptr);
  583. context.nativeContext = nullptr;
  584. }
  585. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  586. {
  587. auto& comp = *getComponent();
  588. if (isAttached (comp) != canBeAttached (comp))
  589. componentVisibilityChanged();
  590. if (comp.getWidth() > 0 && comp.getHeight() > 0
  591. && context.nativeContext != nullptr)
  592. {
  593. if (auto* c = CachedImage::get (comp))
  594. c->handleResize();
  595. if (auto* peer = comp.getTopLevelComponent()->getPeer())
  596. context.nativeContext->updateWindowPosition (peer->getAreaCoveredBy (comp));
  597. }
  598. }
  599. using ComponentMovementWatcher::componentMovedOrResized;
  600. void componentPeerChanged() override
  601. {
  602. detach();
  603. componentVisibilityChanged();
  604. }
  605. void componentVisibilityChanged() override
  606. {
  607. auto& comp = *getComponent();
  608. if (canBeAttached (comp))
  609. {
  610. if (isAttached (comp))
  611. comp.repaint(); // (needed when windows are un-minimised)
  612. else
  613. attach();
  614. }
  615. else
  616. {
  617. detach();
  618. }
  619. }
  620. using ComponentMovementWatcher::componentVisibilityChanged;
  621. #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
  622. void componentBeingDeleted (Component& c) override
  623. {
  624. /* You must call detach() or delete your OpenGLContext to remove it
  625. from a component BEFORE deleting the component that it is using!
  626. */
  627. jassertfalse;
  628. ComponentMovementWatcher::componentBeingDeleted (c);
  629. }
  630. #endif
  631. void update()
  632. {
  633. auto& comp = *getComponent();
  634. if (canBeAttached (comp))
  635. start();
  636. else
  637. stop();
  638. }
  639. private:
  640. OpenGLContext& context;
  641. bool canBeAttached (const Component& comp) noexcept
  642. {
  643. return (! context.overrideCanAttach) && comp.getWidth() > 0 && comp.getHeight() > 0 && isShowingOrMinimised (comp);
  644. }
  645. static bool isShowingOrMinimised (const Component& c)
  646. {
  647. if (! c.isVisible())
  648. return false;
  649. if (auto* p = c.getParentComponent())
  650. return isShowingOrMinimised (*p);
  651. return c.getPeer() != nullptr;
  652. }
  653. static bool isAttached (const Component& comp) noexcept
  654. {
  655. return comp.getCachedComponentImage() != nullptr;
  656. }
  657. void attach()
  658. {
  659. auto& comp = *getComponent();
  660. auto* newCachedImage = new CachedImage (context, comp,
  661. context.openGLPixelFormat,
  662. context.contextToShareWith);
  663. comp.setCachedComponentImage (newCachedImage);
  664. start();
  665. }
  666. void stop()
  667. {
  668. stopTimer();
  669. auto& comp = *getComponent();
  670. #if JUCE_MAC
  671. [[(NSView*) comp.getWindowHandle() window] disableScreenUpdatesUntilFlush];
  672. #endif
  673. if (auto* oldCachedImage = CachedImage::get (comp))
  674. oldCachedImage->stop(); // (must stop this before detaching it from the component)
  675. }
  676. void start()
  677. {
  678. auto& comp = *getComponent();
  679. if (auto* cachedImage = CachedImage::get (comp))
  680. {
  681. cachedImage->start(); // (must wait until this is attached before starting its thread)
  682. cachedImage->updateViewportSize (true);
  683. startTimer (400);
  684. }
  685. }
  686. void timerCallback() override
  687. {
  688. if (auto* cachedImage = CachedImage::get (*getComponent()))
  689. cachedImage->checkViewportBounds();
  690. }
  691. };
  692. //==============================================================================
  693. OpenGLContext::OpenGLContext()
  694. {
  695. }
  696. OpenGLContext::~OpenGLContext()
  697. {
  698. detach();
  699. }
  700. void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept
  701. {
  702. // This method must not be called when the context has already been attached!
  703. // Call it before attaching your context, or use detach() first, before calling this!
  704. jassert (nativeContext == nullptr);
  705. renderer = rendererToUse;
  706. }
  707. void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept
  708. {
  709. // This method must not be called when the context has already been attached!
  710. // Call it before attaching your context, or use detach() first, before calling this!
  711. jassert (nativeContext == nullptr);
  712. renderComponents = shouldPaintComponent;
  713. }
  714. void OpenGLContext::setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept
  715. {
  716. continuousRepaint = shouldContinuouslyRepaint;
  717. #if JUCE_MAC
  718. if (auto* component = getTargetComponent())
  719. {
  720. detach();
  721. attachment.reset (new Attachment (*this, *component));
  722. }
  723. #endif
  724. triggerRepaint();
  725. }
  726. void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept
  727. {
  728. // This method must not be called when the context has already been attached!
  729. // Call it before attaching your context, or use detach() first, before calling this!
  730. jassert (nativeContext == nullptr);
  731. openGLPixelFormat = preferredPixelFormat;
  732. }
  733. void OpenGLContext::setTextureMagnificationFilter (OpenGLContext::TextureMagnificationFilter magFilterMode) noexcept
  734. {
  735. texMagFilter = magFilterMode;
  736. }
  737. void OpenGLContext::setNativeSharedContext (void* nativeContextToShareWith) noexcept
  738. {
  739. // This method must not be called when the context has already been attached!
  740. // Call it before attaching your context, or use detach() first, before calling this!
  741. jassert (nativeContext == nullptr);
  742. contextToShareWith = nativeContextToShareWith;
  743. }
  744. void OpenGLContext::setMultisamplingEnabled (bool b) noexcept
  745. {
  746. // This method must not be called when the context has already been attached!
  747. // Call it before attaching your context, or use detach() first, before calling this!
  748. jassert (nativeContext == nullptr);
  749. useMultisampling = b;
  750. }
  751. void OpenGLContext::setOpenGLVersionRequired (OpenGLVersion v) noexcept
  752. {
  753. versionRequired = v;
  754. }
  755. void OpenGLContext::attachTo (Component& component)
  756. {
  757. component.repaint();
  758. if (getTargetComponent() != &component)
  759. {
  760. detach();
  761. attachment.reset (new Attachment (*this, component));
  762. }
  763. }
  764. void OpenGLContext::detach()
  765. {
  766. if (auto* a = attachment.get())
  767. {
  768. a->detach(); // must detach before nulling our pointer
  769. attachment.reset();
  770. }
  771. nativeContext = nullptr;
  772. }
  773. bool OpenGLContext::isAttached() const noexcept
  774. {
  775. return nativeContext != nullptr;
  776. }
  777. Component* OpenGLContext::getTargetComponent() const noexcept
  778. {
  779. return attachment != nullptr ? attachment->getComponent() : nullptr;
  780. }
  781. OpenGLContext* OpenGLContext::getContextAttachedTo (Component& c) noexcept
  782. {
  783. if (auto* ci = CachedImage::get (c))
  784. return &(ci->context);
  785. return nullptr;
  786. }
  787. static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext;
  788. OpenGLContext* OpenGLContext::getCurrentContext()
  789. {
  790. return currentThreadActiveContext.get();
  791. }
  792. bool OpenGLContext::makeActive() const noexcept
  793. {
  794. auto& current = currentThreadActiveContext.get();
  795. if (nativeContext != nullptr && nativeContext->makeActive())
  796. {
  797. current = const_cast<OpenGLContext*> (this);
  798. return true;
  799. }
  800. current = nullptr;
  801. return false;
  802. }
  803. bool OpenGLContext::isActive() const noexcept
  804. {
  805. return nativeContext != nullptr && nativeContext->isActive();
  806. }
  807. void OpenGLContext::deactivateCurrentContext()
  808. {
  809. NativeContext::deactivateCurrentContext();
  810. currentThreadActiveContext.get() = nullptr;
  811. }
  812. void OpenGLContext::triggerRepaint()
  813. {
  814. if (auto* cachedImage = getCachedImage())
  815. cachedImage->triggerRepaint();
  816. }
  817. void OpenGLContext::swapBuffers()
  818. {
  819. if (nativeContext != nullptr)
  820. nativeContext->swapBuffers();
  821. }
  822. unsigned int OpenGLContext::getFrameBufferID() const noexcept
  823. {
  824. return nativeContext != nullptr ? nativeContext->getFrameBufferID() : 0;
  825. }
  826. bool OpenGLContext::setSwapInterval (int numFramesPerSwap)
  827. {
  828. return nativeContext != nullptr && nativeContext->setSwapInterval (numFramesPerSwap);
  829. }
  830. int OpenGLContext::getSwapInterval() const
  831. {
  832. return nativeContext != nullptr ? nativeContext->getSwapInterval() : 0;
  833. }
  834. void* OpenGLContext::getRawContext() const noexcept
  835. {
  836. return nativeContext != nullptr ? nativeContext->getRawContext() : nullptr;
  837. }
  838. OpenGLContext::CachedImage* OpenGLContext::getCachedImage() const noexcept
  839. {
  840. if (auto* comp = getTargetComponent())
  841. return CachedImage::get (*comp);
  842. return nullptr;
  843. }
  844. bool OpenGLContext::areShadersAvailable() const
  845. {
  846. auto* c = getCachedImage();
  847. return c != nullptr && c->shadersAvailable;
  848. }
  849. ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const
  850. {
  851. jassert (name != nullptr);
  852. auto* c = getCachedImage();
  853. // This method must only be called from an openGL rendering callback.
  854. jassert (c != nullptr && nativeContext != nullptr);
  855. jassert (getCurrentContext() != nullptr);
  856. auto index = c->associatedObjectNames.indexOf (name);
  857. return index >= 0 ? c->associatedObjects.getUnchecked (index).get() : nullptr;
  858. }
  859. void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObject* newObject)
  860. {
  861. jassert (name != nullptr);
  862. if (auto* c = getCachedImage())
  863. {
  864. // This method must only be called from an openGL rendering callback.
  865. jassert (nativeContext != nullptr);
  866. jassert (getCurrentContext() != nullptr);
  867. const int index = c->associatedObjectNames.indexOf (name);
  868. if (index >= 0)
  869. {
  870. if (newObject != nullptr)
  871. {
  872. c->associatedObjects.set (index, newObject);
  873. }
  874. else
  875. {
  876. c->associatedObjectNames.remove (index);
  877. c->associatedObjects.remove (index);
  878. }
  879. }
  880. else if (newObject != nullptr)
  881. {
  882. c->associatedObjectNames.add (name);
  883. c->associatedObjects.add (newObject);
  884. }
  885. }
  886. }
  887. void OpenGLContext::setImageCacheSize (size_t newSize) noexcept { imageCacheMaxSize = newSize; }
  888. size_t OpenGLContext::getImageCacheSize() const noexcept { return imageCacheMaxSize; }
  889. void OpenGLContext::execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock)
  890. {
  891. if (auto* c = getCachedImage())
  892. c->execute (std::move (workerToUse), shouldBlock);
  893. else
  894. jassertfalse; // You must have attached the context to a component
  895. }
  896. //==============================================================================
  897. struct DepthTestDisabler
  898. {
  899. DepthTestDisabler() noexcept
  900. {
  901. glGetBooleanv (GL_DEPTH_TEST, &wasEnabled);
  902. if (wasEnabled)
  903. glDisable (GL_DEPTH_TEST);
  904. }
  905. ~DepthTestDisabler() noexcept
  906. {
  907. if (wasEnabled)
  908. glEnable (GL_DEPTH_TEST);
  909. }
  910. GLboolean wasEnabled;
  911. };
  912. //==============================================================================
  913. void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
  914. const Rectangle<int>& anchorPosAndTextureSize,
  915. const int contextWidth, const int contextHeight,
  916. bool flippedVertically)
  917. {
  918. if (contextWidth <= 0 || contextHeight <= 0)
  919. return;
  920. JUCE_CHECK_OPENGL_ERROR
  921. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  922. glEnable (GL_BLEND);
  923. DepthTestDisabler depthDisabler;
  924. if (areShadersAvailable())
  925. {
  926. struct OverlayShaderProgram : public ReferenceCountedObject
  927. {
  928. OverlayShaderProgram (OpenGLContext& context)
  929. : program (context), builder (program), params (program)
  930. {}
  931. static const OverlayShaderProgram& select (OpenGLContext& context)
  932. {
  933. static const char programValueID[] = "juceGLComponentOverlayShader";
  934. OverlayShaderProgram* program = static_cast<OverlayShaderProgram*> (context.getAssociatedObject (programValueID));
  935. if (program == nullptr)
  936. {
  937. program = new OverlayShaderProgram (context);
  938. context.setAssociatedObject (programValueID, program);
  939. }
  940. program->program.use();
  941. return *program;
  942. }
  943. struct ProgramBuilder
  944. {
  945. ProgramBuilder (OpenGLShaderProgram& prog)
  946. {
  947. prog.addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (
  948. "attribute " JUCE_HIGHP " vec2 position;"
  949. "uniform " JUCE_HIGHP " vec2 screenSize;"
  950. "uniform " JUCE_HIGHP " float textureBounds[4];"
  951. "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
  952. "varying " JUCE_HIGHP " vec2 texturePos;"
  953. "void main()"
  954. "{"
  955. JUCE_HIGHP " vec2 scaled = position / (0.5 * screenSize.xy);"
  956. "gl_Position = vec4 (scaled.x - 1.0, 1.0 - scaled.y, 0, 1.0);"
  957. "texturePos = (position - vec2 (textureBounds[0], textureBounds[1])) / vec2 (textureBounds[2], textureBounds[3]);"
  958. "texturePos = vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y);"
  959. "}"));
  960. prog.addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (
  961. "uniform sampler2D imageTexture;"
  962. "varying " JUCE_HIGHP " vec2 texturePos;"
  963. "void main()"
  964. "{"
  965. "gl_FragColor = texture2D (imageTexture, texturePos);"
  966. "}"));
  967. prog.link();
  968. }
  969. };
  970. struct Params
  971. {
  972. Params (OpenGLShaderProgram& prog)
  973. : positionAttribute (prog, "position"),
  974. screenSize (prog, "screenSize"),
  975. imageTexture (prog, "imageTexture"),
  976. textureBounds (prog, "textureBounds"),
  977. vOffsetAndScale (prog, "vOffsetAndScale")
  978. {}
  979. void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flipVertically) const
  980. {
  981. const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
  982. textureBounds.set (m, 4);
  983. imageTexture.set (0);
  984. screenSize.set (targetWidth, targetHeight);
  985. vOffsetAndScale.set (flipVertically ? 0.0f : 1.0f,
  986. flipVertically ? 1.0f : -1.0f);
  987. }
  988. OpenGLShaderProgram::Attribute positionAttribute;
  989. OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
  990. };
  991. OpenGLShaderProgram program;
  992. ProgramBuilder builder;
  993. Params params;
  994. };
  995. auto left = (GLshort) targetClipArea.getX();
  996. auto top = (GLshort) targetClipArea.getY();
  997. auto right = (GLshort) targetClipArea.getRight();
  998. auto bottom = (GLshort) targetClipArea.getBottom();
  999. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  1000. auto& program = OverlayShaderProgram::select (*this);
  1001. program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
  1002. GLuint vertexBuffer = 0;
  1003. extensions.glGenBuffers (1, &vertexBuffer);
  1004. extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  1005. extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
  1006. auto index = (GLuint) program.params.positionAttribute.attributeID;
  1007. extensions.glVertexAttribPointer (index, 2, GL_SHORT, GL_FALSE, 4, nullptr);
  1008. extensions.glEnableVertexAttribArray (index);
  1009. JUCE_CHECK_OPENGL_ERROR
  1010. if (extensions.glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
  1011. {
  1012. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  1013. extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  1014. extensions.glUseProgram (0);
  1015. extensions.glDisableVertexAttribArray (index);
  1016. extensions.glDeleteBuffers (1, &vertexBuffer);
  1017. }
  1018. else
  1019. {
  1020. clearGLError();
  1021. }
  1022. }
  1023. else
  1024. {
  1025. jassert (attachment == nullptr); // Running on an old graphics card!
  1026. }
  1027. JUCE_CHECK_OPENGL_ERROR
  1028. }
  1029. #if JUCE_ANDROID
  1030. EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY;
  1031. EGLDisplay OpenGLContext::NativeContext::config;
  1032. void OpenGLContext::NativeContext::surfaceCreated (LocalRef<jobject> holder)
  1033. {
  1034. ignoreUnused (holder);
  1035. if (auto* cachedImage = CachedImage::get (component))
  1036. {
  1037. if (auto* pool = cachedImage->renderThread.get())
  1038. {
  1039. if (! pool->contains (cachedImage))
  1040. {
  1041. cachedImage->resume();
  1042. cachedImage->context.triggerRepaint();
  1043. }
  1044. }
  1045. }
  1046. }
  1047. void OpenGLContext::NativeContext::surfaceDestroyed (LocalRef<jobject> holder)
  1048. {
  1049. ignoreUnused (holder);
  1050. // unlike the name suggests this will be called just before the
  1051. // surface is destroyed. We need to pause the render thread.
  1052. if (auto* cachedImage = CachedImage::get (component))
  1053. {
  1054. cachedImage->pause();
  1055. if (auto* threadPool = cachedImage->renderThread.get())
  1056. threadPool->waitForJobToFinish (cachedImage, -1);
  1057. }
  1058. }
  1059. #endif
  1060. } // namespace juce