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.

1207 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. stopTimer();
  532. Component& comp = *getComponent();
  533. #if JUCE_MAC
  534. [[(NSView*) comp.getWindowHandle() window] disableScreenUpdatesUntilFlush];
  535. #endif
  536. if (CachedImage* const oldCachedImage = CachedImage::get (comp))
  537. oldCachedImage->stop(); // (must stop this before detaching it from the component)
  538. comp.setCachedComponentImage (nullptr);
  539. context.nativeContext = nullptr;
  540. }
  541. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  542. {
  543. Component& comp = *getComponent();
  544. if (isAttached (comp) != canBeAttached (comp))
  545. componentVisibilityChanged();
  546. if (comp.getWidth() > 0 && comp.getHeight() > 0
  547. && context.nativeContext != nullptr)
  548. {
  549. if (CachedImage* const c = CachedImage::get (comp))
  550. c->handleResize();
  551. if (ComponentPeer* peer = comp.getTopLevelComponent()->getPeer())
  552. context.nativeContext->updateWindowPosition (peer->getAreaCoveredBy (comp));
  553. }
  554. }
  555. void componentPeerChanged() override
  556. {
  557. detach();
  558. componentVisibilityChanged();
  559. }
  560. void componentVisibilityChanged() override
  561. {
  562. Component& comp = *getComponent();
  563. if (canBeAttached (comp))
  564. {
  565. if (isAttached (comp))
  566. comp.repaint(); // (needed when windows are un-minimised)
  567. else
  568. attach();
  569. }
  570. else
  571. {
  572. detach();
  573. }
  574. }
  575. #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
  576. void componentBeingDeleted (Component& c) override
  577. {
  578. /* You must call detach() or delete your OpenGLContext to remove it
  579. from a component BEFORE deleting the component that it is using!
  580. */
  581. jassertfalse;
  582. ComponentMovementWatcher::componentBeingDeleted (c);
  583. }
  584. #endif
  585. private:
  586. OpenGLContext& context;
  587. static bool canBeAttached (const Component& comp) noexcept
  588. {
  589. return comp.getWidth() > 0 && comp.getHeight() > 0 && isShowingOrMinimised (comp);
  590. }
  591. static bool isShowingOrMinimised (const Component& c)
  592. {
  593. if (! c.isVisible())
  594. return false;
  595. if (Component* p = c.getParentComponent())
  596. return isShowingOrMinimised (*p);
  597. return c.getPeer() != nullptr;
  598. }
  599. static bool isAttached (const Component& comp) noexcept
  600. {
  601. return comp.getCachedComponentImage() != nullptr;
  602. }
  603. void attach()
  604. {
  605. Component& comp = *getComponent();
  606. CachedImage* const newCachedImage = new CachedImage (context, comp,
  607. context.openGLPixelFormat,
  608. context.contextToShareWith);
  609. comp.setCachedComponentImage (newCachedImage);
  610. newCachedImage->start(); // (must wait until this is attached before starting its thread)
  611. newCachedImage->updateViewportSize (true);
  612. startTimer (400);
  613. }
  614. void timerCallback() override
  615. {
  616. if (CachedImage* const cachedImage = CachedImage::get (*getComponent()))
  617. cachedImage->checkViewportBounds();
  618. }
  619. };
  620. //==============================================================================
  621. OpenGLContext::OpenGLContext()
  622. : nativeContext (nullptr), renderer (nullptr),
  623. currentRenderScale (1.0), contextToShareWith (nullptr),
  624. versionRequired (OpenGLContext::defaultGLVersion),
  625. imageCacheMaxSize (8 * 1024 * 1024),
  626. renderComponents (true),
  627. useMultisampling (false),
  628. continuousRepaint (false)
  629. {
  630. }
  631. OpenGLContext::~OpenGLContext()
  632. {
  633. detach();
  634. }
  635. void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept
  636. {
  637. // This method must not be called when the context has already been attached!
  638. // Call it before attaching your context, or use detach() first, before calling this!
  639. jassert (nativeContext == nullptr);
  640. renderer = rendererToUse;
  641. }
  642. void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept
  643. {
  644. // This method must not be called when the context has already been attached!
  645. // Call it before attaching your context, or use detach() first, before calling this!
  646. jassert (nativeContext == nullptr);
  647. renderComponents = shouldPaintComponent;
  648. }
  649. void OpenGLContext::setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept
  650. {
  651. continuousRepaint = shouldContinuouslyRepaint;
  652. triggerRepaint();
  653. }
  654. void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept
  655. {
  656. // This method must not be called when the context has already been attached!
  657. // Call it before attaching your context, or use detach() first, before calling this!
  658. jassert (nativeContext == nullptr);
  659. openGLPixelFormat = preferredPixelFormat;
  660. }
  661. void OpenGLContext::setNativeSharedContext (void* nativeContextToShareWith) noexcept
  662. {
  663. // This method must not be called when the context has already been attached!
  664. // Call it before attaching your context, or use detach() first, before calling this!
  665. jassert (nativeContext == nullptr);
  666. contextToShareWith = nativeContextToShareWith;
  667. }
  668. void OpenGLContext::setMultisamplingEnabled (bool b) noexcept
  669. {
  670. // This method must not be called when the context has already been attached!
  671. // Call it before attaching your context, or use detach() first, before calling this!
  672. jassert (nativeContext == nullptr);
  673. useMultisampling = b;
  674. }
  675. void OpenGLContext::setOpenGLVersionRequired (OpenGLVersion v) noexcept
  676. {
  677. versionRequired = v;
  678. }
  679. void OpenGLContext::attachTo (Component& component)
  680. {
  681. component.repaint();
  682. if (getTargetComponent() != &component)
  683. {
  684. detach();
  685. attachment = new Attachment (*this, component);
  686. }
  687. }
  688. void OpenGLContext::detach()
  689. {
  690. if (Attachment* a = attachment)
  691. {
  692. a->detach(); // must detach before nulling our pointer
  693. attachment = nullptr;
  694. }
  695. nativeContext = nullptr;
  696. }
  697. bool OpenGLContext::isAttached() const noexcept
  698. {
  699. return nativeContext != nullptr;
  700. }
  701. Component* OpenGLContext::getTargetComponent() const noexcept
  702. {
  703. return attachment != nullptr ? attachment->getComponent() : nullptr;
  704. }
  705. OpenGLContext* OpenGLContext::getContextAttachedTo (Component& c) noexcept
  706. {
  707. if (CachedImage* const ci = CachedImage::get (c))
  708. return &(ci->context);
  709. return nullptr;
  710. }
  711. static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext;
  712. OpenGLContext* OpenGLContext::getCurrentContext()
  713. {
  714. return currentThreadActiveContext.get();
  715. }
  716. bool OpenGLContext::makeActive() const noexcept
  717. {
  718. OpenGLContext*& current = currentThreadActiveContext.get();
  719. if (nativeContext != nullptr && nativeContext->makeActive())
  720. {
  721. current = const_cast<OpenGLContext*> (this);
  722. return true;
  723. }
  724. current = nullptr;
  725. return false;
  726. }
  727. bool OpenGLContext::isActive() const noexcept
  728. {
  729. return nativeContext != nullptr && nativeContext->isActive();
  730. }
  731. void OpenGLContext::deactivateCurrentContext()
  732. {
  733. NativeContext::deactivateCurrentContext();
  734. currentThreadActiveContext.get() = nullptr;
  735. }
  736. void OpenGLContext::triggerRepaint()
  737. {
  738. if (CachedImage* const cachedImage = getCachedImage())
  739. cachedImage->triggerRepaint();
  740. }
  741. void OpenGLContext::swapBuffers()
  742. {
  743. if (nativeContext != nullptr)
  744. nativeContext->swapBuffers();
  745. }
  746. unsigned int OpenGLContext::getFrameBufferID() const noexcept
  747. {
  748. return nativeContext != nullptr ? nativeContext->getFrameBufferID() : 0;
  749. }
  750. bool OpenGLContext::setSwapInterval (int numFramesPerSwap)
  751. {
  752. return nativeContext != nullptr && nativeContext->setSwapInterval (numFramesPerSwap);
  753. }
  754. int OpenGLContext::getSwapInterval() const
  755. {
  756. return nativeContext != nullptr ? nativeContext->getSwapInterval() : 0;
  757. }
  758. void* OpenGLContext::getRawContext() const noexcept
  759. {
  760. return nativeContext != nullptr ? nativeContext->getRawContext() : nullptr;
  761. }
  762. OpenGLContext::CachedImage* OpenGLContext::getCachedImage() const noexcept
  763. {
  764. if (Component* const comp = getTargetComponent())
  765. return CachedImage::get (*comp);
  766. return nullptr;
  767. }
  768. bool OpenGLContext::areShadersAvailable() const
  769. {
  770. CachedImage* const c = getCachedImage();
  771. return c != nullptr && c->shadersAvailable;
  772. }
  773. ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const
  774. {
  775. jassert (name != nullptr);
  776. CachedImage* const c = getCachedImage();
  777. // This method must only be called from an openGL rendering callback.
  778. jassert (c != nullptr && nativeContext != nullptr);
  779. jassert (getCurrentContext() != nullptr);
  780. const int index = c->associatedObjectNames.indexOf (name);
  781. return index >= 0 ? c->associatedObjects.getUnchecked (index) : nullptr;
  782. }
  783. void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObject* newObject)
  784. {
  785. jassert (name != nullptr);
  786. if (CachedImage* const c = getCachedImage())
  787. {
  788. // This method must only be called from an openGL rendering callback.
  789. jassert (nativeContext != nullptr);
  790. jassert (getCurrentContext() != nullptr);
  791. const int index = c->associatedObjectNames.indexOf (name);
  792. if (index >= 0)
  793. {
  794. if (newObject != nullptr)
  795. {
  796. c->associatedObjects.set (index, newObject);
  797. }
  798. else
  799. {
  800. c->associatedObjectNames.remove (index);
  801. c->associatedObjects.remove (index);
  802. }
  803. }
  804. else if (newObject != nullptr)
  805. {
  806. c->associatedObjectNames.add (name);
  807. c->associatedObjects.add (newObject);
  808. }
  809. }
  810. }
  811. void OpenGLContext::setImageCacheSize (size_t newSize) noexcept { imageCacheMaxSize = newSize; }
  812. size_t OpenGLContext::getImageCacheSize() const noexcept { return imageCacheMaxSize; }
  813. void OpenGLContext::execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock)
  814. {
  815. if (CachedImage* const c = getCachedImage())
  816. c->execute (static_cast<OpenGLContext::AsyncWorker::Ptr&&> (workerToUse), shouldBlock);
  817. else
  818. jassertfalse; // You must have attached the context to a component
  819. }
  820. //==============================================================================
  821. struct DepthTestDisabler
  822. {
  823. DepthTestDisabler() noexcept
  824. {
  825. glGetBooleanv (GL_DEPTH_TEST, &wasEnabled);
  826. if (wasEnabled)
  827. glDisable (GL_DEPTH_TEST);
  828. }
  829. ~DepthTestDisabler() noexcept
  830. {
  831. if (wasEnabled)
  832. glEnable (GL_DEPTH_TEST);
  833. }
  834. GLboolean wasEnabled;
  835. };
  836. //==============================================================================
  837. void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
  838. const Rectangle<int>& anchorPosAndTextureSize,
  839. const int contextWidth, const int contextHeight,
  840. bool flippedVertically)
  841. {
  842. if (contextWidth <= 0 || contextHeight <= 0)
  843. return;
  844. JUCE_CHECK_OPENGL_ERROR
  845. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  846. glEnable (GL_BLEND);
  847. DepthTestDisabler depthDisabler;
  848. if (areShadersAvailable())
  849. {
  850. struct OverlayShaderProgram : public ReferenceCountedObject
  851. {
  852. OverlayShaderProgram (OpenGLContext& context)
  853. : program (context), builder (program), params (program)
  854. {}
  855. static const OverlayShaderProgram& select (OpenGLContext& context)
  856. {
  857. static const char programValueID[] = "juceGLComponentOverlayShader";
  858. OverlayShaderProgram* program = static_cast<OverlayShaderProgram*> (context.getAssociatedObject (programValueID));
  859. if (program == nullptr)
  860. {
  861. program = new OverlayShaderProgram (context);
  862. context.setAssociatedObject (programValueID, program);
  863. }
  864. program->program.use();
  865. return *program;
  866. }
  867. struct ProgramBuilder
  868. {
  869. ProgramBuilder (OpenGLShaderProgram& prog)
  870. {
  871. prog.addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (
  872. "attribute " JUCE_HIGHP " vec2 position;"
  873. "uniform " JUCE_HIGHP " vec2 screenSize;"
  874. "uniform " JUCE_HIGHP " float textureBounds[4];"
  875. "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
  876. "varying " JUCE_HIGHP " vec2 texturePos;"
  877. "void main()"
  878. "{"
  879. JUCE_HIGHP " vec2 scaled = position / (0.5 * screenSize.xy);"
  880. "gl_Position = vec4 (scaled.x - 1.0, 1.0 - scaled.y, 0, 1.0);"
  881. "texturePos = (position - vec2 (textureBounds[0], textureBounds[1])) / vec2 (textureBounds[2], textureBounds[3]);"
  882. "texturePos = vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y);"
  883. "}"));
  884. prog.addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (
  885. "uniform sampler2D imageTexture;"
  886. "varying " JUCE_HIGHP " vec2 texturePos;"
  887. "void main()"
  888. "{"
  889. "gl_FragColor = texture2D (imageTexture, texturePos);"
  890. "}"));
  891. prog.link();
  892. }
  893. };
  894. struct Params
  895. {
  896. Params (OpenGLShaderProgram& prog)
  897. : positionAttribute (prog, "position"),
  898. screenSize (prog, "screenSize"),
  899. imageTexture (prog, "imageTexture"),
  900. textureBounds (prog, "textureBounds"),
  901. vOffsetAndScale (prog, "vOffsetAndScale")
  902. {}
  903. void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flipVertically) const
  904. {
  905. const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
  906. textureBounds.set (m, 4);
  907. imageTexture.set (0);
  908. screenSize.set (targetWidth, targetHeight);
  909. vOffsetAndScale.set (flipVertically ? 0.0f : 1.0f,
  910. flipVertically ? 1.0f : -1.0f);
  911. }
  912. OpenGLShaderProgram::Attribute positionAttribute;
  913. OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
  914. };
  915. OpenGLShaderProgram program;
  916. ProgramBuilder builder;
  917. Params params;
  918. };
  919. const GLshort left = (GLshort) targetClipArea.getX();
  920. const GLshort top = (GLshort) targetClipArea.getY();
  921. const GLshort right = (GLshort) targetClipArea.getRight();
  922. const GLshort bottom = (GLshort) targetClipArea.getBottom();
  923. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  924. const OverlayShaderProgram& program = OverlayShaderProgram::select (*this);
  925. program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
  926. GLuint vertexBuffer = 0;
  927. extensions.glGenBuffers (1, &vertexBuffer);
  928. extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  929. extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
  930. const GLuint index = (GLuint) program.params.positionAttribute.attributeID;
  931. extensions.glVertexAttribPointer (index, 2, GL_SHORT, GL_FALSE, 4, 0);
  932. extensions.glEnableVertexAttribArray (index);
  933. JUCE_CHECK_OPENGL_ERROR
  934. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  935. extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  936. extensions.glUseProgram (0);
  937. extensions.glDisableVertexAttribArray (index);
  938. extensions.glDeleteBuffers (1, &vertexBuffer);
  939. }
  940. else
  941. {
  942. jassert (attachment == nullptr); // Running on an old graphics card!
  943. }
  944. JUCE_CHECK_OPENGL_ERROR
  945. }
  946. #if JUCE_ANDROID
  947. EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY;
  948. EGLDisplay OpenGLContext::NativeContext::config;
  949. void OpenGLContext::NativeContext::surfaceCreated (jobject holder)
  950. {
  951. ignoreUnused (holder);
  952. if (juceContext != nullptr)
  953. {
  954. if (OpenGLContext::CachedImage* cachedImage = juceContext->getCachedImage())
  955. cachedImage->resume();
  956. juceContext->triggerRepaint();
  957. }
  958. }
  959. void OpenGLContext::NativeContext::surfaceDestroyed (jobject holder)
  960. {
  961. ignoreUnused (holder);
  962. // unlike the name suggests this will be called just before the
  963. // surface is destroyed. We need to pause the render thread.
  964. if (juceContext != nullptr)
  965. if (OpenGLContext::CachedImage* cachedImage = juceContext->getCachedImage())
  966. cachedImage->pause();
  967. }
  968. #endif