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.

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