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.

1360 lines
41KB

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