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.

1194 lines
37KB

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