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.

1578 lines
49KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #if JUCE_MAC
  19. #include <juce_gui_basics/native/juce_mac_PerScreenDisplayLinks.h>
  20. #endif
  21. namespace juce
  22. {
  23. #if JUCE_IOS
  24. struct AppInactivityCallback // NB: this is a duplicate of an internal declaration in juce_core
  25. {
  26. virtual ~AppInactivityCallback() {}
  27. virtual void appBecomingInactive() = 0;
  28. };
  29. extern Array<AppInactivityCallback*> appBecomingInactiveCallbacks;
  30. // On iOS, all GL calls will crash when the app is running in the background, so
  31. // this prevents them from happening (which some messy locking behaviour)
  32. struct iOSBackgroundProcessCheck : public AppInactivityCallback
  33. {
  34. iOSBackgroundProcessCheck() { isBackgroundProcess(); appBecomingInactiveCallbacks.add (this); }
  35. ~iOSBackgroundProcessCheck() override { appBecomingInactiveCallbacks.removeAllInstancesOf (this); }
  36. bool isBackgroundProcess()
  37. {
  38. const bool b = Process::isForegroundProcess();
  39. isForeground.set (b ? 1 : 0);
  40. return ! b;
  41. }
  42. void appBecomingInactive() override
  43. {
  44. int counter = 2000;
  45. while (--counter > 0 && isForeground.get() != 0)
  46. Thread::sleep (1);
  47. }
  48. private:
  49. Atomic<int> isForeground;
  50. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSBackgroundProcessCheck)
  51. };
  52. #endif
  53. #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
  54. extern JUCE_API double getScaleFactorForWindow (HWND);
  55. #endif
  56. static bool contextHasTextureNpotFeature()
  57. {
  58. if (getOpenGLVersion() >= Version (2))
  59. return true;
  60. // If the version is < 2, we can't use the newer extension-checking API
  61. // so we have to use glGetString
  62. const auto* extensionsBegin = glGetString (GL_EXTENSIONS);
  63. if (extensionsBegin == nullptr)
  64. return false;
  65. const auto* extensionsEnd = findNullTerminator (extensionsBegin);
  66. const std::string extensionsString (extensionsBegin, extensionsEnd);
  67. const auto stringTokens = StringArray::fromTokens (extensionsString.c_str(), false);
  68. return stringTokens.contains ("GL_ARB_texture_non_power_of_two");
  69. }
  70. //==============================================================================
  71. class OpenGLContext::CachedImage : public CachedComponentImage,
  72. private ThreadPoolJob
  73. {
  74. struct AreaAndScale
  75. {
  76. Rectangle<int> area;
  77. double scale;
  78. auto tie() const { return std::tie (area, scale); }
  79. auto operator== (const AreaAndScale& other) const { return tie() == other.tie(); }
  80. auto operator!= (const AreaAndScale& other) const { return tie() != other.tie(); }
  81. };
  82. class LockedAreaAndScale
  83. {
  84. public:
  85. auto get() const
  86. {
  87. const ScopedLock lock (mutex);
  88. return data;
  89. }
  90. template <typename Fn>
  91. void set (const AreaAndScale& d, Fn&& ifDifferent)
  92. {
  93. const auto old = [&]
  94. {
  95. const ScopedLock lock (mutex);
  96. return std::exchange (data, d);
  97. }();
  98. if (old != d)
  99. ifDifferent();
  100. }
  101. private:
  102. CriticalSection mutex;
  103. AreaAndScale data { {}, 1.0 };
  104. };
  105. public:
  106. CachedImage (OpenGLContext& c, Component& comp,
  107. const OpenGLPixelFormat& pixFormat, void* contextToShare)
  108. : ThreadPoolJob ("OpenGL Rendering"),
  109. context (c),
  110. component (comp)
  111. {
  112. nativeContext.reset (new NativeContext (component, pixFormat, contextToShare,
  113. c.useMultisampling, c.versionRequired));
  114. if (nativeContext->createdOk())
  115. context.nativeContext = nativeContext.get();
  116. else
  117. nativeContext.reset();
  118. }
  119. ~CachedImage() override
  120. {
  121. stop();
  122. }
  123. //==============================================================================
  124. void start()
  125. {
  126. if (nativeContext != nullptr)
  127. {
  128. renderThread = std::make_unique<ThreadPool> (1);
  129. resume();
  130. }
  131. }
  132. void stop()
  133. {
  134. if (renderThread != nullptr)
  135. {
  136. // make sure everything has finished executing
  137. destroying = true;
  138. if (workQueue.size() > 0)
  139. {
  140. if (! renderThread->contains (this))
  141. resume();
  142. while (workQueue.size() != 0)
  143. Thread::sleep (20);
  144. }
  145. pause();
  146. renderThread.reset();
  147. }
  148. hasInitialised = false;
  149. }
  150. //==============================================================================
  151. void pause()
  152. {
  153. signalJobShouldExit();
  154. contextsWaitingForFlush->cancelFlush (this);
  155. messageManagerLock.abort();
  156. if (renderThread != nullptr)
  157. {
  158. repaintEvent.signal();
  159. renderThread->removeJob (this, true, -1);
  160. }
  161. }
  162. void resume()
  163. {
  164. if (renderThread != nullptr)
  165. renderThread->addJob (this, false);
  166. }
  167. //==============================================================================
  168. void paint (Graphics&) override
  169. {
  170. updateViewportSize();
  171. }
  172. bool invalidateAll() override
  173. {
  174. validArea.clear();
  175. triggerRepaint();
  176. return false;
  177. }
  178. bool invalidate (const Rectangle<int>& area) override
  179. {
  180. validArea.subtract (area.toFloat().transformedBy (transform).getSmallestIntegerContainer());
  181. triggerRepaint();
  182. return false;
  183. }
  184. void releaseResources() override
  185. {
  186. stop();
  187. }
  188. void triggerRepaint()
  189. {
  190. needsUpdate = 1;
  191. repaintEvent.signal();
  192. }
  193. //==============================================================================
  194. bool ensureFrameBufferSize (Rectangle<int> viewportArea)
  195. {
  196. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  197. auto fbW = cachedImageFrameBuffer.getWidth();
  198. auto fbH = cachedImageFrameBuffer.getHeight();
  199. if (fbW != viewportArea.getWidth() || fbH != viewportArea.getHeight() || ! cachedImageFrameBuffer.isValid())
  200. {
  201. if (! cachedImageFrameBuffer.initialise (context, viewportArea.getWidth(), viewportArea.getHeight()))
  202. return false;
  203. validArea.clear();
  204. JUCE_CHECK_OPENGL_ERROR
  205. }
  206. return true;
  207. }
  208. void clearRegionInFrameBuffer (const RectangleList<int>& list)
  209. {
  210. glClearColor (0, 0, 0, 0);
  211. glEnable (GL_SCISSOR_TEST);
  212. auto previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget();
  213. cachedImageFrameBuffer.makeCurrentRenderingTarget();
  214. auto imageH = cachedImageFrameBuffer.getHeight();
  215. for (auto& r : list)
  216. {
  217. glScissor (r.getX(), imageH - r.getBottom(), r.getWidth(), r.getHeight());
  218. glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  219. }
  220. glDisable (GL_SCISSOR_TEST);
  221. context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget);
  222. JUCE_CHECK_OPENGL_ERROR
  223. }
  224. bool renderFrame()
  225. {
  226. MessageManager::Lock::ScopedTryLockType mmLock (messageManagerLock, false);
  227. const auto isUpdating = needsUpdate.exchange (false);
  228. if (context.renderComponents && isUpdating)
  229. {
  230. // This avoids hogging the message thread when doing intensive rendering.
  231. if (lastMMLockReleaseTime + 1 >= Time::getMillisecondCounter())
  232. Thread::sleep (2);
  233. while (! shouldExit())
  234. {
  235. doWorkWhileWaitingForLock (false);
  236. if (mmLock.retryLock())
  237. break;
  238. }
  239. if (shouldExit())
  240. return false;
  241. }
  242. if (! context.makeActive())
  243. return false;
  244. {
  245. NativeContext::Locker locker (*nativeContext);
  246. JUCE_CHECK_OPENGL_ERROR
  247. doWorkWhileWaitingForLock (true);
  248. const auto currentAreaAndScale = areaAndScale.get();
  249. const auto viewportArea = currentAreaAndScale.area;
  250. if (context.renderer != nullptr)
  251. {
  252. glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
  253. context.currentRenderScale = currentAreaAndScale.scale;
  254. context.renderer->renderOpenGL();
  255. clearGLError();
  256. bindVertexArray();
  257. }
  258. if (context.renderComponents)
  259. {
  260. if (isUpdating)
  261. {
  262. paintComponent (currentAreaAndScale);
  263. if (! hasInitialised)
  264. return false;
  265. messageManagerLock.exit();
  266. lastMMLockReleaseTime = Time::getMillisecondCounter();
  267. }
  268. glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
  269. drawComponentBuffer();
  270. }
  271. OpenGLContext::deactivateCurrentContext();
  272. }
  273. if (! shouldExit())
  274. contextsWaitingForFlush->flush (this);
  275. return true;
  276. }
  277. void updateViewportSize()
  278. {
  279. JUCE_ASSERT_MESSAGE_THREAD
  280. if (auto* peer = component.getPeer())
  281. {
  282. #if JUCE_MAC
  283. updateScreen();
  284. const auto displayScale = Desktop::getInstance().getGlobalScaleFactor() * [this]
  285. {
  286. if (auto* view = getCurrentView())
  287. {
  288. if ([view respondsToSelector: @selector (backingScaleFactor)])
  289. return [(id) view backingScaleFactor];
  290. if (auto* window = [view window])
  291. return [window backingScaleFactor];
  292. }
  293. return areaAndScale.get().scale;
  294. }();
  295. #else
  296. const auto displayScale = Desktop::getInstance().getDisplays().getDisplayForRect (component.getTopLevelComponent()->getScreenBounds())->scale;
  297. #endif
  298. auto localBounds = component.getLocalBounds();
  299. auto newArea = peer->getComponent().getLocalArea (&component, localBounds).withZeroOrigin() * displayScale;
  300. #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
  301. auto newScale = getScaleFactorForWindow (nativeContext->getNativeHandle());
  302. auto desktopScale = Desktop::getInstance().getGlobalScaleFactor();
  303. if (! approximatelyEqual (1.0f, desktopScale))
  304. newScale *= desktopScale;
  305. #else
  306. auto newScale = displayScale;
  307. #endif
  308. areaAndScale.set ({ newArea, newScale }, [&]
  309. {
  310. // Transform is only accessed when the message manager is locked
  311. transform = AffineTransform::scale ((float) newArea.getWidth() / (float) localBounds.getWidth(),
  312. (float) newArea.getHeight() / (float) localBounds.getHeight());
  313. nativeContext->updateWindowPosition (peer->getAreaCoveredBy (component));
  314. invalidateAll();
  315. });
  316. }
  317. }
  318. void bindVertexArray() noexcept
  319. {
  320. if (shouldUseCustomVAO())
  321. if (vertexArrayObject != 0)
  322. context.extensions.glBindVertexArray (vertexArrayObject);
  323. }
  324. void checkViewportBounds()
  325. {
  326. auto screenBounds = component.getTopLevelComponent()->getScreenBounds();
  327. if (lastScreenBounds != screenBounds)
  328. {
  329. updateViewportSize();
  330. lastScreenBounds = screenBounds;
  331. }
  332. }
  333. void paintComponent (const AreaAndScale& currentAreaAndScale)
  334. {
  335. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  336. // you mustn't set your own cached image object when attaching a GL context!
  337. jassert (get (component) == this);
  338. if (! ensureFrameBufferSize (currentAreaAndScale.area))
  339. return;
  340. RectangleList<int> invalid (currentAreaAndScale.area);
  341. invalid.subtract (validArea);
  342. validArea = currentAreaAndScale.area;
  343. if (! invalid.isEmpty())
  344. {
  345. clearRegionInFrameBuffer (invalid);
  346. {
  347. std::unique_ptr<LowLevelGraphicsContext> g (createOpenGLGraphicsContext (context, cachedImageFrameBuffer));
  348. g->clipToRectangleList (invalid);
  349. g->addTransform (transform);
  350. paintOwner (*g);
  351. JUCE_CHECK_OPENGL_ERROR
  352. }
  353. if (! context.isActive())
  354. context.makeActive();
  355. }
  356. JUCE_CHECK_OPENGL_ERROR
  357. }
  358. void drawComponentBuffer()
  359. {
  360. if (contextRequiresTexture2DEnableDisable())
  361. glEnable (GL_TEXTURE_2D);
  362. #if JUCE_WINDOWS
  363. // some stupidly old drivers are missing this function, so try to at least avoid a crash here,
  364. // but if you hit this assertion you may want to have your own version check before using the
  365. // component rendering stuff on such old drivers.
  366. jassert (context.extensions.glActiveTexture != nullptr);
  367. if (context.extensions.glActiveTexture != nullptr)
  368. #endif
  369. {
  370. context.extensions.glActiveTexture (GL_TEXTURE0);
  371. }
  372. glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID());
  373. bindVertexArray();
  374. const Rectangle<int> cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight());
  375. context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight(), false);
  376. glBindTexture (GL_TEXTURE_2D, 0);
  377. JUCE_CHECK_OPENGL_ERROR
  378. }
  379. void paintOwner (LowLevelGraphicsContext& llgc)
  380. {
  381. Graphics g (llgc);
  382. #if JUCE_ENABLE_REPAINT_DEBUGGING
  383. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  384. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  385. #endif
  386. {
  387. g.saveState();
  388. }
  389. #endif
  390. JUCE_TRY
  391. {
  392. component.paintEntireComponent (g, false);
  393. }
  394. JUCE_CATCH_EXCEPTION
  395. #if JUCE_ENABLE_REPAINT_DEBUGGING
  396. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  397. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  398. #endif
  399. {
  400. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  401. // clearly when things are being repainted.
  402. g.restoreState();
  403. static Random rng;
  404. g.fillAll (Colour ((uint8) rng.nextInt (255),
  405. (uint8) rng.nextInt (255),
  406. (uint8) rng.nextInt (255),
  407. (uint8) 0x50));
  408. }
  409. #endif
  410. }
  411. void handleResize()
  412. {
  413. updateViewportSize();
  414. #if JUCE_MAC
  415. if (hasInitialised)
  416. {
  417. [nativeContext->view update];
  418. renderFrame();
  419. }
  420. #endif
  421. }
  422. //==============================================================================
  423. JobStatus runJob() override
  424. {
  425. {
  426. // Allow the message thread to finish setting-up the context before using it.
  427. MessageManager::Lock::ScopedTryLockType mmLock (messageManagerLock, false);
  428. do
  429. {
  430. if (shouldExit())
  431. return ThreadPoolJob::jobHasFinished;
  432. } while (! mmLock.retryLock());
  433. }
  434. if (! initialiseOnThread())
  435. {
  436. hasInitialised = false;
  437. return ThreadPoolJob::jobHasFinished;
  438. }
  439. hasInitialised = true;
  440. while (! shouldExit())
  441. {
  442. #if JUCE_IOS
  443. if (backgroundProcessCheck.isBackgroundProcess())
  444. {
  445. repaintEvent.wait (300);
  446. repaintEvent.reset();
  447. continue;
  448. }
  449. #endif
  450. if (shouldExit())
  451. break;
  452. #if JUCE_MAC
  453. if (context.continuousRepaint)
  454. {
  455. repaintEvent.wait (-1);
  456. renderFrame();
  457. }
  458. else
  459. #endif
  460. if (! renderFrame())
  461. repaintEvent.wait (5); // failed to render, so avoid a tight fail-loop.
  462. else if (! context.continuousRepaint && ! shouldExit())
  463. repaintEvent.wait (-1);
  464. repaintEvent.reset();
  465. }
  466. hasInitialised = false;
  467. context.makeActive();
  468. shutdownOnThread();
  469. OpenGLContext::deactivateCurrentContext();
  470. return ThreadPoolJob::jobHasFinished;
  471. }
  472. bool initialiseOnThread()
  473. {
  474. // On android, this can get called twice, so drop any previous state.
  475. associatedObjectNames.clear();
  476. associatedObjects.clear();
  477. cachedImageFrameBuffer.release();
  478. context.makeActive();
  479. if (! nativeContext->initialiseOnRenderThread (context))
  480. return false;
  481. #if JUCE_ANDROID
  482. // On android the context may be created in initialiseOnRenderThread
  483. // and we therefore need to call makeActive again
  484. context.makeActive();
  485. #endif
  486. gl::loadFunctions();
  487. if (shouldUseCustomVAO())
  488. {
  489. context.extensions.glGenVertexArrays (1, &vertexArrayObject);
  490. bindVertexArray();
  491. }
  492. #if JUCE_DEBUG
  493. if (getOpenGLVersion() >= Version { 4, 3 } && glDebugMessageCallback != nullptr)
  494. {
  495. glEnable (GL_DEBUG_OUTPUT);
  496. glDebugMessageCallback ([] (GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar* message, const void*)
  497. {
  498. // This may reiterate issues that are also flagged by JUCE_CHECK_OPENGL_ERROR.
  499. // The advantage of this callback is that it will catch *all* errors, even if we
  500. // forget to check manually.
  501. DBG ("OpenGL DBG message: " << message);
  502. jassertfalse;
  503. }, nullptr);
  504. }
  505. #endif
  506. const auto currentViewportArea = areaAndScale.get().area;
  507. glViewport (0, 0, currentViewportArea.getWidth(), currentViewportArea.getHeight());
  508. nativeContext->setSwapInterval (1);
  509. #if ! JUCE_OPENGL_ES
  510. JUCE_CHECK_OPENGL_ERROR
  511. shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0;
  512. clearGLError();
  513. #endif
  514. textureNpotSupported = contextHasTextureNpotFeature();
  515. if (context.renderer != nullptr)
  516. context.renderer->newOpenGLContextCreated();
  517. return true;
  518. }
  519. void shutdownOnThread()
  520. {
  521. if (context.renderer != nullptr)
  522. context.renderer->openGLContextClosing();
  523. if (vertexArrayObject != 0)
  524. context.extensions.glDeleteVertexArrays (1, &vertexArrayObject);
  525. associatedObjectNames.clear();
  526. associatedObjects.clear();
  527. cachedImageFrameBuffer.release();
  528. nativeContext->shutdownOnRenderThread();
  529. }
  530. /* Returns true if the context requires a non-zero vertex array object (VAO) to be bound.
  531. If the context is a compatibility context, we can just pretend that VAOs don't exist,
  532. and use the default VAO all the time instead. This provides a more consistent experience
  533. in user code, which might make calls (like glVertexPointer()) that only work when VAO 0 is
  534. bound in OpenGL 3.2+.
  535. */
  536. bool shouldUseCustomVAO() const
  537. {
  538. #if JUCE_OPENGL_ES
  539. return false;
  540. #else
  541. clearGLError();
  542. GLint mask = 0;
  543. glGetIntegerv (GL_CONTEXT_PROFILE_MASK, &mask);
  544. // The context isn't aware of the profile mask, so it pre-dates the core profile
  545. if (glGetError() == GL_INVALID_ENUM)
  546. return false;
  547. // Also assumes a compatibility profile if the mask is completely empty for some reason
  548. return (mask & (GLint) GL_CONTEXT_CORE_PROFILE_BIT) != 0;
  549. #endif
  550. }
  551. //==============================================================================
  552. struct BlockingWorker : public OpenGLContext::AsyncWorker
  553. {
  554. BlockingWorker (OpenGLContext::AsyncWorker::Ptr && workerToUse)
  555. : originalWorker (std::move (workerToUse))
  556. {}
  557. void operator() (OpenGLContext& calleeContext)
  558. {
  559. if (originalWorker != nullptr)
  560. (*originalWorker) (calleeContext);
  561. finishedSignal.signal();
  562. }
  563. void block() noexcept { finishedSignal.wait(); }
  564. OpenGLContext::AsyncWorker::Ptr originalWorker;
  565. WaitableEvent finishedSignal;
  566. };
  567. bool doWorkWhileWaitingForLock (bool contextIsAlreadyActive)
  568. {
  569. bool contextActivated = false;
  570. for (OpenGLContext::AsyncWorker::Ptr work = workQueue.removeAndReturn (0);
  571. work != nullptr && (! shouldExit()); work = workQueue.removeAndReturn (0))
  572. {
  573. if ((! contextActivated) && (! contextIsAlreadyActive))
  574. {
  575. if (! context.makeActive())
  576. break;
  577. contextActivated = true;
  578. }
  579. NativeContext::Locker locker (*nativeContext);
  580. (*work) (context);
  581. clearGLError();
  582. }
  583. if (contextActivated)
  584. OpenGLContext::deactivateCurrentContext();
  585. return shouldExit();
  586. }
  587. void execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock, bool calledFromDestructor = false)
  588. {
  589. if (calledFromDestructor || ! destroying)
  590. {
  591. if (shouldBlock)
  592. {
  593. auto blocker = new BlockingWorker (std::move (workerToUse));
  594. OpenGLContext::AsyncWorker::Ptr worker (*blocker);
  595. workQueue.add (worker);
  596. messageManagerLock.abort();
  597. context.triggerRepaint();
  598. blocker->block();
  599. }
  600. else
  601. {
  602. workQueue.add (std::move (workerToUse));
  603. messageManagerLock.abort();
  604. context.triggerRepaint();
  605. }
  606. }
  607. else
  608. {
  609. jassertfalse; // you called execute AFTER you detached your OpenGLContext
  610. }
  611. }
  612. //==============================================================================
  613. static CachedImage* get (Component& c) noexcept
  614. {
  615. return dynamic_cast<CachedImage*> (c.getCachedComponentImage());
  616. }
  617. //==============================================================================
  618. class ContextsWaitingForFlush : private AsyncUpdater
  619. {
  620. public:
  621. /* Ask to swap the CachedImage's buffers on the main thread.
  622. Will block until the buffers have been swapped, or until the swap has been cancelled.
  623. */
  624. void flush (CachedImage* ctx)
  625. {
  626. {
  627. const std::lock_guard<std::mutex> lock (mutex);
  628. if (find (ctx) == contexts.cend())
  629. contexts.push_back (ctx);
  630. }
  631. if (MessageManager::getInstance()->isThisTheMessageThread())
  632. {
  633. handleAsyncUpdate();
  634. }
  635. else
  636. {
  637. triggerAsyncUpdate();
  638. std::unique_lock<std::mutex> lock { mutex };
  639. condvar.wait (lock, [this, ctx] { return find (ctx) == contexts.cend(); });
  640. }
  641. }
  642. /* When a context is destroyed, this function must be called so that flush() can return. */
  643. void cancelFlush (CachedImage* ctx)
  644. {
  645. const std::lock_guard<std::mutex> lock (mutex);
  646. const auto iter = find (ctx);
  647. if (iter != contexts.cend())
  648. {
  649. contexts.erase (iter);
  650. condvar.notify_all();
  651. }
  652. }
  653. ~ContextsWaitingForFlush() override
  654. {
  655. cancelPendingUpdate();
  656. // There definitely shouldn't still be active CachedImages if this object is being destroyed!
  657. jassert (contexts.empty());
  658. }
  659. private:
  660. std::mutex mutex;
  661. std::condition_variable condvar;
  662. std::vector<CachedImage*> contexts;
  663. /* Precondition: mutex is locked. */
  664. std::vector<CachedImage*>::const_iterator find (CachedImage* ctx) const
  665. {
  666. // Linear search here because the number of OpenGL contexts probably won't
  667. // ever be bigger than double/triple digits
  668. return std::find (contexts.cbegin(), contexts.cend(), ctx);
  669. }
  670. /* Swaps the buffers for each of the pending contexts, then notifies all rendering threads
  671. that they may continue.
  672. */
  673. void handleAsyncUpdate() override
  674. {
  675. std::unique_lock<std::mutex> lock (mutex);
  676. for (auto* ctx : contexts)
  677. {
  678. auto& native = *ctx->nativeContext;
  679. OpenGLContext::NativeContext::Locker nativeContextLocker (native);
  680. native.makeActive();
  681. native.swapBuffers();
  682. native.deactivateCurrentContext();
  683. }
  684. contexts.clear();
  685. condvar.notify_all();
  686. }
  687. };
  688. SharedResourcePointer<ContextsWaitingForFlush> contextsWaitingForFlush;
  689. //==============================================================================
  690. friend class NativeContext;
  691. std::unique_ptr<NativeContext> nativeContext;
  692. OpenGLContext& context;
  693. Component& component;
  694. OpenGLFrameBuffer cachedImageFrameBuffer;
  695. RectangleList<int> validArea;
  696. Rectangle<int> lastScreenBounds;
  697. AffineTransform transform;
  698. GLuint vertexArrayObject = 0;
  699. LockedAreaAndScale areaAndScale;
  700. StringArray associatedObjectNames;
  701. ReferenceCountedArray<ReferenceCountedObject> associatedObjects;
  702. WaitableEvent canPaintNowFlag, finishedPaintingFlag, repaintEvent { true };
  703. #if JUCE_OPENGL_ES
  704. bool shadersAvailable = true;
  705. #else
  706. bool shadersAvailable = false;
  707. #endif
  708. bool textureNpotSupported = false;
  709. std::atomic<bool> hasInitialised { false }, needsUpdate { true }, destroying { false };
  710. uint32 lastMMLockReleaseTime = 0;
  711. #if JUCE_MAC
  712. NSView* getCurrentView() const
  713. {
  714. JUCE_ASSERT_MESSAGE_THREAD;
  715. if (auto* peer = component.getPeer())
  716. return static_cast<NSView*> (peer->getNativeHandle());
  717. return nullptr;
  718. }
  719. NSWindow* getCurrentWindow() const
  720. {
  721. JUCE_ASSERT_MESSAGE_THREAD;
  722. if (auto* view = getCurrentView())
  723. return [view window];
  724. return nullptr;
  725. }
  726. NSScreen* getCurrentScreen() const
  727. {
  728. JUCE_ASSERT_MESSAGE_THREAD;
  729. if (auto* window = getCurrentWindow())
  730. return [window screen];
  731. return nullptr;
  732. }
  733. void updateScreen()
  734. {
  735. const auto screen = getCurrentScreen();
  736. lastScreen = screen;
  737. const auto newRefreshPeriod = sharedDisplayLinks->getNominalVideoRefreshPeriodSForScreen (screen);
  738. if (newRefreshPeriod != 0.0 && std::exchange (refreshPeriod, newRefreshPeriod) != newRefreshPeriod)
  739. nativeContext->setNominalVideoRefreshPeriodS (newRefreshPeriod);
  740. }
  741. std::atomic<NSScreen*> lastScreen { nullptr };
  742. double refreshPeriod = 0.0;
  743. FunctionNotificationCenterObserver observer { NSWindowDidChangeScreenNotification,
  744. getCurrentWindow(),
  745. [this] { updateScreen(); } };
  746. // Note: the NSViewComponentPeer also has a SharedResourcePointer<PerScreenDisplayLinks> to
  747. // avoid unnecessarily duplicating display-link threads.
  748. SharedResourcePointer<PerScreenDisplayLinks> sharedDisplayLinks;
  749. PerScreenDisplayLinks::Connection connection { sharedDisplayLinks->registerFactory ([this] (auto* screen)
  750. {
  751. return [this, screen]
  752. {
  753. // Note: check against lastScreen rather than trying to access the component's peer here,
  754. // because this function is not called on the main thread.
  755. if (context.continuousRepaint)
  756. if (screen == lastScreen)
  757. repaintEvent.signal();
  758. };
  759. }) };
  760. #endif
  761. std::unique_ptr<ThreadPool> renderThread;
  762. ReferenceCountedArray<OpenGLContext::AsyncWorker, CriticalSection> workQueue;
  763. MessageManager::Lock messageManagerLock;
  764. #if JUCE_IOS
  765. iOSBackgroundProcessCheck backgroundProcessCheck;
  766. #endif
  767. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
  768. };
  769. //==============================================================================
  770. class OpenGLContext::Attachment : public ComponentMovementWatcher,
  771. private Timer
  772. {
  773. public:
  774. Attachment (OpenGLContext& c, Component& comp)
  775. : ComponentMovementWatcher (&comp), context (c)
  776. {
  777. if (canBeAttached (comp))
  778. attach();
  779. }
  780. ~Attachment() override
  781. {
  782. detach();
  783. }
  784. void detach()
  785. {
  786. auto& comp = *getComponent();
  787. stop();
  788. comp.setCachedComponentImage (nullptr);
  789. context.nativeContext = nullptr;
  790. }
  791. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  792. {
  793. auto& comp = *getComponent();
  794. if (isAttached (comp) != canBeAttached (comp))
  795. componentVisibilityChanged();
  796. if (comp.getWidth() > 0 && comp.getHeight() > 0
  797. && context.nativeContext != nullptr)
  798. {
  799. if (auto* c = CachedImage::get (comp))
  800. c->handleResize();
  801. if (auto* peer = comp.getTopLevelComponent()->getPeer())
  802. context.nativeContext->updateWindowPosition (peer->getAreaCoveredBy (comp));
  803. }
  804. }
  805. using ComponentMovementWatcher::componentMovedOrResized;
  806. void componentPeerChanged() override
  807. {
  808. detach();
  809. componentVisibilityChanged();
  810. }
  811. void componentVisibilityChanged() override
  812. {
  813. auto& comp = *getComponent();
  814. if (canBeAttached (comp))
  815. {
  816. if (isAttached (comp))
  817. comp.repaint(); // (needed when windows are un-minimised)
  818. else
  819. attach();
  820. }
  821. else
  822. {
  823. detach();
  824. }
  825. }
  826. using ComponentMovementWatcher::componentVisibilityChanged;
  827. #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
  828. void componentBeingDeleted (Component& c) override
  829. {
  830. /* You must call detach() or delete your OpenGLContext to remove it
  831. from a component BEFORE deleting the component that it is using!
  832. */
  833. jassertfalse;
  834. ComponentMovementWatcher::componentBeingDeleted (c);
  835. }
  836. #endif
  837. private:
  838. OpenGLContext& context;
  839. bool canBeAttached (const Component& comp) noexcept
  840. {
  841. return (! context.overrideCanAttach) && comp.getWidth() > 0 && comp.getHeight() > 0 && isShowingOrMinimised (comp);
  842. }
  843. static bool isShowingOrMinimised (const Component& c)
  844. {
  845. if (! c.isVisible())
  846. return false;
  847. if (auto* p = c.getParentComponent())
  848. return isShowingOrMinimised (*p);
  849. return c.getPeer() != nullptr;
  850. }
  851. static bool isAttached (const Component& comp) noexcept
  852. {
  853. return comp.getCachedComponentImage() != nullptr;
  854. }
  855. void attach()
  856. {
  857. auto& comp = *getComponent();
  858. auto* newCachedImage = new CachedImage (context, comp,
  859. context.openGLPixelFormat,
  860. context.contextToShareWith);
  861. comp.setCachedComponentImage (newCachedImage);
  862. start();
  863. }
  864. void stop()
  865. {
  866. stopTimer();
  867. auto& comp = *getComponent();
  868. #if JUCE_MAC
  869. [[(NSView*) comp.getWindowHandle() window] disableScreenUpdatesUntilFlush];
  870. #endif
  871. if (auto* oldCachedImage = CachedImage::get (comp))
  872. oldCachedImage->stop(); // (must stop this before detaching it from the component)
  873. }
  874. void start()
  875. {
  876. auto& comp = *getComponent();
  877. if (auto* cachedImage = CachedImage::get (comp))
  878. {
  879. cachedImage->start(); // (must wait until this is attached before starting its thread)
  880. cachedImage->updateViewportSize();
  881. startTimer (400);
  882. }
  883. }
  884. void timerCallback() override
  885. {
  886. if (auto* cachedImage = CachedImage::get (*getComponent()))
  887. cachedImage->checkViewportBounds();
  888. }
  889. };
  890. //==============================================================================
  891. OpenGLContext::OpenGLContext()
  892. {
  893. }
  894. OpenGLContext::~OpenGLContext()
  895. {
  896. detach();
  897. }
  898. void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept
  899. {
  900. // This method must not be called when the context has already been attached!
  901. // Call it before attaching your context, or use detach() first, before calling this!
  902. jassert (nativeContext == nullptr);
  903. renderer = rendererToUse;
  904. }
  905. void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept
  906. {
  907. // This method must not be called when the context has already been attached!
  908. // Call it before attaching your context, or use detach() first, before calling this!
  909. jassert (nativeContext == nullptr);
  910. renderComponents = shouldPaintComponent;
  911. }
  912. void OpenGLContext::setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept
  913. {
  914. continuousRepaint = shouldContinuouslyRepaint;
  915. #if JUCE_MAC
  916. if (auto* component = getTargetComponent())
  917. {
  918. detach();
  919. attachment.reset (new Attachment (*this, *component));
  920. }
  921. #endif
  922. triggerRepaint();
  923. }
  924. void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept
  925. {
  926. // This method must not be called when the context has already been attached!
  927. // Call it before attaching your context, or use detach() first, before calling this!
  928. jassert (nativeContext == nullptr);
  929. openGLPixelFormat = preferredPixelFormat;
  930. }
  931. void OpenGLContext::setTextureMagnificationFilter (OpenGLContext::TextureMagnificationFilter magFilterMode) noexcept
  932. {
  933. texMagFilter = magFilterMode;
  934. }
  935. void OpenGLContext::setNativeSharedContext (void* nativeContextToShareWith) noexcept
  936. {
  937. // This method must not be called when the context has already been attached!
  938. // Call it before attaching your context, or use detach() first, before calling this!
  939. jassert (nativeContext == nullptr);
  940. contextToShareWith = nativeContextToShareWith;
  941. }
  942. void OpenGLContext::setMultisamplingEnabled (bool b) noexcept
  943. {
  944. // This method must not be called when the context has already been attached!
  945. // Call it before attaching your context, or use detach() first, before calling this!
  946. jassert (nativeContext == nullptr);
  947. useMultisampling = b;
  948. }
  949. void OpenGLContext::setOpenGLVersionRequired (OpenGLVersion v) noexcept
  950. {
  951. versionRequired = v;
  952. }
  953. void OpenGLContext::attachTo (Component& component)
  954. {
  955. component.repaint();
  956. if (getTargetComponent() != &component)
  957. {
  958. detach();
  959. attachment.reset (new Attachment (*this, component));
  960. }
  961. }
  962. void OpenGLContext::detach()
  963. {
  964. if (auto* a = attachment.get())
  965. {
  966. a->detach(); // must detach before nulling our pointer
  967. attachment.reset();
  968. }
  969. nativeContext = nullptr;
  970. }
  971. bool OpenGLContext::isAttached() const noexcept
  972. {
  973. return nativeContext != nullptr;
  974. }
  975. Component* OpenGLContext::getTargetComponent() const noexcept
  976. {
  977. return attachment != nullptr ? attachment->getComponent() : nullptr;
  978. }
  979. OpenGLContext* OpenGLContext::getContextAttachedTo (Component& c) noexcept
  980. {
  981. if (auto* ci = CachedImage::get (c))
  982. return &(ci->context);
  983. return nullptr;
  984. }
  985. static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext;
  986. OpenGLContext* OpenGLContext::getCurrentContext()
  987. {
  988. return currentThreadActiveContext.get();
  989. }
  990. bool OpenGLContext::makeActive() const noexcept
  991. {
  992. auto& current = currentThreadActiveContext.get();
  993. if (nativeContext != nullptr && nativeContext->makeActive())
  994. {
  995. current = const_cast<OpenGLContext*> (this);
  996. return true;
  997. }
  998. current = nullptr;
  999. return false;
  1000. }
  1001. bool OpenGLContext::isActive() const noexcept
  1002. {
  1003. return nativeContext != nullptr && nativeContext->isActive();
  1004. }
  1005. void OpenGLContext::deactivateCurrentContext()
  1006. {
  1007. NativeContext::deactivateCurrentContext();
  1008. currentThreadActiveContext.get() = nullptr;
  1009. }
  1010. void OpenGLContext::triggerRepaint()
  1011. {
  1012. if (auto* cachedImage = getCachedImage())
  1013. cachedImage->triggerRepaint();
  1014. }
  1015. void OpenGLContext::swapBuffers()
  1016. {
  1017. if (nativeContext != nullptr)
  1018. nativeContext->swapBuffers();
  1019. }
  1020. unsigned int OpenGLContext::getFrameBufferID() const noexcept
  1021. {
  1022. return nativeContext != nullptr ? nativeContext->getFrameBufferID() : 0;
  1023. }
  1024. bool OpenGLContext::setSwapInterval (int numFramesPerSwap)
  1025. {
  1026. return nativeContext != nullptr && nativeContext->setSwapInterval (numFramesPerSwap);
  1027. }
  1028. int OpenGLContext::getSwapInterval() const
  1029. {
  1030. return nativeContext != nullptr ? nativeContext->getSwapInterval() : 0;
  1031. }
  1032. void* OpenGLContext::getRawContext() const noexcept
  1033. {
  1034. return nativeContext != nullptr ? nativeContext->getRawContext() : nullptr;
  1035. }
  1036. OpenGLContext::CachedImage* OpenGLContext::getCachedImage() const noexcept
  1037. {
  1038. if (auto* comp = getTargetComponent())
  1039. return CachedImage::get (*comp);
  1040. return nullptr;
  1041. }
  1042. bool OpenGLContext::areShadersAvailable() const
  1043. {
  1044. auto* c = getCachedImage();
  1045. return c != nullptr && c->shadersAvailable;
  1046. }
  1047. bool OpenGLContext::isTextureNpotSupported() const
  1048. {
  1049. auto* c = getCachedImage();
  1050. return c != nullptr && c->textureNpotSupported;
  1051. }
  1052. ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const
  1053. {
  1054. jassert (name != nullptr);
  1055. auto* c = getCachedImage();
  1056. // This method must only be called from an openGL rendering callback.
  1057. jassert (c != nullptr && nativeContext != nullptr);
  1058. jassert (getCurrentContext() != nullptr);
  1059. auto index = c->associatedObjectNames.indexOf (name);
  1060. return index >= 0 ? c->associatedObjects.getUnchecked (index).get() : nullptr;
  1061. }
  1062. void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObject* newObject)
  1063. {
  1064. jassert (name != nullptr);
  1065. if (auto* c = getCachedImage())
  1066. {
  1067. // This method must only be called from an openGL rendering callback.
  1068. jassert (nativeContext != nullptr);
  1069. jassert (getCurrentContext() != nullptr);
  1070. const int index = c->associatedObjectNames.indexOf (name);
  1071. if (index >= 0)
  1072. {
  1073. if (newObject != nullptr)
  1074. {
  1075. c->associatedObjects.set (index, newObject);
  1076. }
  1077. else
  1078. {
  1079. c->associatedObjectNames.remove (index);
  1080. c->associatedObjects.remove (index);
  1081. }
  1082. }
  1083. else if (newObject != nullptr)
  1084. {
  1085. c->associatedObjectNames.add (name);
  1086. c->associatedObjects.add (newObject);
  1087. }
  1088. }
  1089. }
  1090. void OpenGLContext::setImageCacheSize (size_t newSize) noexcept { imageCacheMaxSize = newSize; }
  1091. size_t OpenGLContext::getImageCacheSize() const noexcept { return imageCacheMaxSize; }
  1092. void OpenGLContext::execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock)
  1093. {
  1094. if (auto* c = getCachedImage())
  1095. c->execute (std::move (workerToUse), shouldBlock);
  1096. else
  1097. jassertfalse; // You must have attached the context to a component
  1098. }
  1099. //==============================================================================
  1100. struct DepthTestDisabler
  1101. {
  1102. DepthTestDisabler() noexcept
  1103. {
  1104. glGetBooleanv (GL_DEPTH_TEST, &wasEnabled);
  1105. if (wasEnabled)
  1106. glDisable (GL_DEPTH_TEST);
  1107. }
  1108. ~DepthTestDisabler() noexcept
  1109. {
  1110. if (wasEnabled)
  1111. glEnable (GL_DEPTH_TEST);
  1112. }
  1113. GLboolean wasEnabled;
  1114. };
  1115. //==============================================================================
  1116. void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
  1117. const Rectangle<int>& anchorPosAndTextureSize,
  1118. const int contextWidth, const int contextHeight,
  1119. bool flippedVertically)
  1120. {
  1121. if (contextWidth <= 0 || contextHeight <= 0)
  1122. return;
  1123. JUCE_CHECK_OPENGL_ERROR
  1124. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  1125. glEnable (GL_BLEND);
  1126. DepthTestDisabler depthDisabler;
  1127. if (areShadersAvailable())
  1128. {
  1129. struct OverlayShaderProgram : public ReferenceCountedObject
  1130. {
  1131. OverlayShaderProgram (OpenGLContext& context)
  1132. : program (context), params (program)
  1133. {}
  1134. static const OverlayShaderProgram& select (OpenGLContext& context)
  1135. {
  1136. static const char programValueID[] = "juceGLComponentOverlayShader";
  1137. OverlayShaderProgram* program = static_cast<OverlayShaderProgram*> (context.getAssociatedObject (programValueID));
  1138. if (program == nullptr)
  1139. {
  1140. program = new OverlayShaderProgram (context);
  1141. context.setAssociatedObject (programValueID, program);
  1142. }
  1143. program->program.use();
  1144. return *program;
  1145. }
  1146. struct BuiltProgram : public OpenGLShaderProgram
  1147. {
  1148. explicit BuiltProgram (OpenGLContext& ctx)
  1149. : OpenGLShaderProgram (ctx)
  1150. {
  1151. addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (
  1152. "attribute " JUCE_HIGHP " vec2 position;"
  1153. "uniform " JUCE_HIGHP " vec2 screenSize;"
  1154. "uniform " JUCE_HIGHP " float textureBounds[4];"
  1155. "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
  1156. "varying " JUCE_HIGHP " vec2 texturePos;"
  1157. "void main()"
  1158. "{"
  1159. JUCE_HIGHP " vec2 scaled = position / (0.5 * screenSize.xy);"
  1160. "gl_Position = vec4 (scaled.x - 1.0, 1.0 - scaled.y, 0, 1.0);"
  1161. "texturePos = (position - vec2 (textureBounds[0], textureBounds[1])) / vec2 (textureBounds[2], textureBounds[3]);"
  1162. "texturePos = vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y);"
  1163. "}"));
  1164. addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (
  1165. "uniform sampler2D imageTexture;"
  1166. "varying " JUCE_HIGHP " vec2 texturePos;"
  1167. "void main()"
  1168. "{"
  1169. "gl_FragColor = texture2D (imageTexture, texturePos);"
  1170. "}"));
  1171. link();
  1172. }
  1173. };
  1174. struct Params
  1175. {
  1176. Params (OpenGLShaderProgram& prog)
  1177. : positionAttribute (prog, "position"),
  1178. screenSize (prog, "screenSize"),
  1179. imageTexture (prog, "imageTexture"),
  1180. textureBounds (prog, "textureBounds"),
  1181. vOffsetAndScale (prog, "vOffsetAndScale")
  1182. {}
  1183. void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flipVertically) const
  1184. {
  1185. const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
  1186. textureBounds.set (m, 4);
  1187. imageTexture.set (0);
  1188. screenSize.set (targetWidth, targetHeight);
  1189. vOffsetAndScale.set (flipVertically ? 0.0f : 1.0f,
  1190. flipVertically ? 1.0f : -1.0f);
  1191. }
  1192. OpenGLShaderProgram::Attribute positionAttribute;
  1193. OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
  1194. };
  1195. BuiltProgram program;
  1196. Params params;
  1197. };
  1198. auto left = (GLshort) targetClipArea.getX();
  1199. auto top = (GLshort) targetClipArea.getY();
  1200. auto right = (GLshort) targetClipArea.getRight();
  1201. auto bottom = (GLshort) targetClipArea.getBottom();
  1202. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  1203. auto& program = OverlayShaderProgram::select (*this);
  1204. program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
  1205. GLuint vertexBuffer = 0;
  1206. extensions.glGenBuffers (1, &vertexBuffer);
  1207. extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  1208. extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
  1209. auto index = (GLuint) program.params.positionAttribute.attributeID;
  1210. extensions.glVertexAttribPointer (index, 2, GL_SHORT, GL_FALSE, 4, nullptr);
  1211. extensions.glEnableVertexAttribArray (index);
  1212. JUCE_CHECK_OPENGL_ERROR
  1213. if (extensions.glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
  1214. {
  1215. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  1216. extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  1217. extensions.glUseProgram (0);
  1218. extensions.glDisableVertexAttribArray (index);
  1219. extensions.glDeleteBuffers (1, &vertexBuffer);
  1220. }
  1221. else
  1222. {
  1223. clearGLError();
  1224. }
  1225. }
  1226. else
  1227. {
  1228. jassert (attachment == nullptr); // Running on an old graphics card!
  1229. }
  1230. JUCE_CHECK_OPENGL_ERROR
  1231. }
  1232. #if JUCE_ANDROID
  1233. EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY;
  1234. EGLDisplay OpenGLContext::NativeContext::config;
  1235. void OpenGLContext::NativeContext::surfaceCreated (LocalRef<jobject> holder)
  1236. {
  1237. ignoreUnused (holder);
  1238. if (auto* cachedImage = CachedImage::get (component))
  1239. {
  1240. if (auto* pool = cachedImage->renderThread.get())
  1241. {
  1242. if (! pool->contains (cachedImage))
  1243. {
  1244. cachedImage->resume();
  1245. cachedImage->context.triggerRepaint();
  1246. }
  1247. }
  1248. }
  1249. }
  1250. void OpenGLContext::NativeContext::surfaceDestroyed (LocalRef<jobject> holder)
  1251. {
  1252. ignoreUnused (holder);
  1253. // unlike the name suggests this will be called just before the
  1254. // surface is destroyed. We need to pause the render thread.
  1255. if (auto* cachedImage = CachedImage::get (component))
  1256. {
  1257. cachedImage->pause();
  1258. if (auto* threadPool = cachedImage->renderThread.get())
  1259. threadPool->waitForJobToFinish (cachedImage, -1);
  1260. }
  1261. }
  1262. #endif
  1263. } // namespace juce