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.

1247 lines
38KB

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