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.

1412 lines
43KB

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