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.

1301 lines
39KB

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