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.

1749 lines
55KB

  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_PerScreenDisplayLinks_mac.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. {
  73. template <typename T, typename U>
  74. static constexpr bool isFlagSet (const T& t, const U& u) { return (t & u) != 0; }
  75. struct AreaAndScale
  76. {
  77. Rectangle<int> area;
  78. double scale;
  79. auto tie() const { return std::tie (area, scale); }
  80. auto operator== (const AreaAndScale& other) const { return tie() == other.tie(); }
  81. auto operator!= (const AreaAndScale& other) const { return tie() != other.tie(); }
  82. };
  83. class LockedAreaAndScale
  84. {
  85. public:
  86. auto get() const
  87. {
  88. const ScopedLock lock (mutex);
  89. return data;
  90. }
  91. template <typename Fn>
  92. void set (const AreaAndScale& d, Fn&& ifDifferent)
  93. {
  94. const auto old = [&]
  95. {
  96. const ScopedLock lock (mutex);
  97. return std::exchange (data, d);
  98. }();
  99. if (old != d)
  100. ifDifferent();
  101. }
  102. private:
  103. CriticalSection mutex;
  104. AreaAndScale data { {}, 1.0 };
  105. };
  106. public:
  107. CachedImage (OpenGLContext& c, Component& comp,
  108. const OpenGLPixelFormat& pixFormat, void* contextToShare)
  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. refreshDisplayLinkConnection();
  119. }
  120. ~CachedImage() override
  121. {
  122. stop();
  123. }
  124. //==============================================================================
  125. void start()
  126. {
  127. if (nativeContext != nullptr)
  128. resume();
  129. }
  130. void stop()
  131. {
  132. // make sure everything has finished executing
  133. state |= StateFlags::pendingDestruction;
  134. if (workQueue.size() > 0)
  135. {
  136. if (! renderThread->contains (this))
  137. resume();
  138. while (workQueue.size() != 0)
  139. Thread::sleep (20);
  140. }
  141. pause();
  142. }
  143. //==============================================================================
  144. void pause()
  145. {
  146. renderThread->remove (this);
  147. if ((state.fetch_and (~StateFlags::initialised) & StateFlags::initialised) == 0)
  148. return;
  149. ScopedContextActivator activator;
  150. activator.activate (context);
  151. if (context.renderer != nullptr)
  152. context.renderer->openGLContextClosing();
  153. if (vertexArrayObject != 0)
  154. context.extensions.glDeleteVertexArrays (1, &vertexArrayObject);
  155. associatedObjectNames.clear();
  156. associatedObjects.clear();
  157. cachedImageFrameBuffer.release();
  158. nativeContext->shutdownOnRenderThread();
  159. }
  160. void resume()
  161. {
  162. renderThread->add (this);
  163. }
  164. //==============================================================================
  165. void paint (Graphics&) override
  166. {
  167. if (MessageManager::getInstance()->isThisTheMessageThread())
  168. {
  169. updateViewportSize();
  170. }
  171. else
  172. {
  173. // If you hit this assertion, it's because paint has been called from a thread other
  174. // than the message thread. This commonly happens when nesting OpenGL contexts, because
  175. // the 'outer' OpenGL renderer will attempt to call paint on the 'inner' context's
  176. // component from the OpenGL thread.
  177. // Nesting OpenGL contexts is not directly supported, however there is a workaround:
  178. // https://forum.juce.com/t/opengl-how-do-3d-with-custom-shaders-and-2d-with-juce-paint-methods-work-together/28026/7
  179. jassertfalse;
  180. }
  181. }
  182. bool invalidateAll() override
  183. {
  184. validArea.clear();
  185. triggerRepaint();
  186. return false;
  187. }
  188. bool invalidate (const Rectangle<int>& area) override
  189. {
  190. validArea.subtract (area.toFloat().transformedBy (transform).getSmallestIntegerContainer());
  191. triggerRepaint();
  192. return false;
  193. }
  194. void releaseResources() override
  195. {
  196. stop();
  197. }
  198. void triggerRepaint()
  199. {
  200. state |= (StateFlags::pendingRender | StateFlags::paintComponents);
  201. renderThread->triggerRepaint();
  202. }
  203. //==============================================================================
  204. bool ensureFrameBufferSize (Rectangle<int> viewportArea)
  205. {
  206. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  207. auto fbW = cachedImageFrameBuffer.getWidth();
  208. auto fbH = cachedImageFrameBuffer.getHeight();
  209. if (fbW != viewportArea.getWidth() || fbH != viewportArea.getHeight() || ! cachedImageFrameBuffer.isValid())
  210. {
  211. if (! cachedImageFrameBuffer.initialise (context, viewportArea.getWidth(), viewportArea.getHeight()))
  212. return false;
  213. validArea.clear();
  214. JUCE_CHECK_OPENGL_ERROR
  215. }
  216. return true;
  217. }
  218. void clearRegionInFrameBuffer (const RectangleList<int>& list)
  219. {
  220. glClearColor (0, 0, 0, 0);
  221. glEnable (GL_SCISSOR_TEST);
  222. auto previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget();
  223. cachedImageFrameBuffer.makeCurrentRenderingTarget();
  224. auto imageH = cachedImageFrameBuffer.getHeight();
  225. for (auto& r : list)
  226. {
  227. glScissor (r.getX(), imageH - r.getBottom(), r.getWidth(), r.getHeight());
  228. glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  229. }
  230. glDisable (GL_SCISSOR_TEST);
  231. context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget);
  232. JUCE_CHECK_OPENGL_ERROR
  233. }
  234. struct ScopedContextActivator
  235. {
  236. bool activate (OpenGLContext& ctx)
  237. {
  238. if (! active)
  239. active = ctx.makeActive();
  240. return active;
  241. }
  242. ~ScopedContextActivator()
  243. {
  244. if (active)
  245. OpenGLContext::deactivateCurrentContext();
  246. }
  247. private:
  248. bool active = false;
  249. };
  250. enum class RenderStatus
  251. {
  252. nominal,
  253. messageThreadAborted,
  254. noWork,
  255. };
  256. RenderStatus renderFrame (MessageManager::Lock& mmLock)
  257. {
  258. if (! isFlagSet (state, StateFlags::initialised))
  259. {
  260. switch (initialiseOnThread())
  261. {
  262. case InitResult::fatal:
  263. case InitResult::retry: return RenderStatus::noWork;
  264. case InitResult::success: break;
  265. }
  266. }
  267. state |= StateFlags::initialised;
  268. #if JUCE_IOS
  269. if (backgroundProcessCheck.isBackgroundProcess())
  270. return RenderStatus::noWork;
  271. #endif
  272. std::optional<MessageManager::Lock::ScopedTryLockType> scopedLock;
  273. ScopedContextActivator contextActivator;
  274. const auto stateToUse = state.fetch_and (StateFlags::persistent);
  275. #if JUCE_MAC
  276. // On macOS, we use a display link callback to trigger repaints, rather than
  277. // letting them run at full throttle
  278. const auto noAutomaticRepaint = true;
  279. #else
  280. const auto noAutomaticRepaint = ! context.continuousRepaint;
  281. #endif
  282. if (! isFlagSet (stateToUse, StateFlags::pendingRender) && noAutomaticRepaint)
  283. return RenderStatus::noWork;
  284. const auto isUpdating = isFlagSet (stateToUse, StateFlags::paintComponents);
  285. if (context.renderComponents && isUpdating)
  286. {
  287. bool abortScope = false;
  288. // If we early-exit here, we need to restore these flags so that the render is
  289. // attempted again in the next time slice.
  290. const ScopeGuard scope { [&] { if (! abortScope) state |= stateToUse; } };
  291. // This avoids hogging the message thread when doing intensive rendering.
  292. std::this_thread::sleep_until (lastMMLockReleaseTime + std::chrono::milliseconds { 2 });
  293. if (renderThread->isListChanging())
  294. return RenderStatus::messageThreadAborted;
  295. doWorkWhileWaitingForLock (contextActivator);
  296. scopedLock.emplace (mmLock);
  297. // If we can't get the lock here, it's probably because a context has been removed
  298. // on the main thread.
  299. // We return, just in case this renderer needs to be removed from the rendering thread.
  300. // If another renderer is being removed instead, then we should be able to get the lock
  301. // next time round.
  302. if (! scopedLock->isLocked())
  303. return RenderStatus::messageThreadAborted;
  304. abortScope = true;
  305. }
  306. {
  307. NativeContext::Locker locker (*nativeContext);
  308. if (! contextActivator.activate (context))
  309. return RenderStatus::noWork;
  310. JUCE_CHECK_OPENGL_ERROR
  311. doWorkWhileWaitingForLock (contextActivator);
  312. const auto currentAreaAndScale = areaAndScale.get();
  313. const auto viewportArea = currentAreaAndScale.area;
  314. if (context.renderer != nullptr)
  315. {
  316. glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
  317. context.currentRenderScale = currentAreaAndScale.scale;
  318. context.renderer->renderOpenGL();
  319. clearGLError();
  320. bindVertexArray();
  321. }
  322. if (context.renderComponents)
  323. {
  324. if (isUpdating)
  325. {
  326. paintComponent (currentAreaAndScale);
  327. if (! isFlagSet (state, StateFlags::initialised))
  328. return RenderStatus::noWork;
  329. scopedLock.reset();
  330. lastMMLockReleaseTime = std::chrono::steady_clock::now();
  331. }
  332. glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
  333. drawComponentBuffer();
  334. }
  335. }
  336. bufferSwapper.swap();
  337. return RenderStatus::nominal;
  338. }
  339. void updateViewportSize()
  340. {
  341. JUCE_ASSERT_MESSAGE_THREAD
  342. if (auto* peer = component.getPeer())
  343. {
  344. #if JUCE_MAC
  345. updateScreen();
  346. const auto displayScale = Desktop::getInstance().getGlobalScaleFactor() * [this]
  347. {
  348. if (auto* view = getCurrentView())
  349. {
  350. if ([view respondsToSelector: @selector (backingScaleFactor)])
  351. return [(id) view backingScaleFactor];
  352. if (auto* window = [view window])
  353. return [window backingScaleFactor];
  354. }
  355. return areaAndScale.get().scale;
  356. }();
  357. #else
  358. const auto displayScale = Desktop::getInstance().getDisplays()
  359. .getDisplayForRect (component.getTopLevelComponent()
  360. ->getScreenBounds())
  361. ->scale;
  362. #endif
  363. const auto localBounds = component.getLocalBounds();
  364. const auto newArea = peer->getComponent().getLocalArea (&component, localBounds).withZeroOrigin() * displayScale;
  365. #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
  366. // Some hosts (Pro Tools 2022.7) do not take the current DPI into account when sizing
  367. // plugin editor windows. Instead of querying the OS for the DPI of the editor window,
  368. // we approximate based on the physical size of the window that was actually provided
  369. // for the context to draw into. This may break if the OpenGL context's component is
  370. // scaled differently in its width and height - but in this case, a single scale factor
  371. // isn't that helpful anyway.
  372. const auto newScale = (float) newArea.getWidth() / (float) localBounds.getWidth();
  373. #else
  374. const auto newScale = (float) displayScale;
  375. #endif
  376. areaAndScale.set ({ newArea, newScale }, [&]
  377. {
  378. // Transform is only accessed when the message manager is locked
  379. transform = AffineTransform::scale ((float) newArea.getWidth() / (float) localBounds.getWidth(),
  380. (float) newArea.getHeight() / (float) localBounds.getHeight());
  381. nativeContext->updateWindowPosition (peer->getAreaCoveredBy (component));
  382. invalidateAll();
  383. });
  384. }
  385. }
  386. void bindVertexArray() noexcept
  387. {
  388. if (shouldUseCustomVAO())
  389. if (vertexArrayObject != 0)
  390. context.extensions.glBindVertexArray (vertexArrayObject);
  391. }
  392. void checkViewportBounds()
  393. {
  394. auto screenBounds = component.getTopLevelComponent()->getScreenBounds();
  395. if (lastScreenBounds != screenBounds)
  396. {
  397. updateViewportSize();
  398. lastScreenBounds = screenBounds;
  399. }
  400. }
  401. void paintComponent (const AreaAndScale& currentAreaAndScale)
  402. {
  403. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  404. // you mustn't set your own cached image object when attaching a GL context!
  405. jassert (get (component) == this);
  406. if (! ensureFrameBufferSize (currentAreaAndScale.area))
  407. return;
  408. RectangleList<int> invalid (currentAreaAndScale.area);
  409. invalid.subtract (validArea);
  410. validArea = currentAreaAndScale.area;
  411. if (! invalid.isEmpty())
  412. {
  413. clearRegionInFrameBuffer (invalid);
  414. {
  415. std::unique_ptr<LowLevelGraphicsContext> g (createOpenGLGraphicsContext (context, cachedImageFrameBuffer));
  416. g->clipToRectangleList (invalid);
  417. g->addTransform (transform);
  418. paintOwner (*g);
  419. JUCE_CHECK_OPENGL_ERROR
  420. }
  421. }
  422. JUCE_CHECK_OPENGL_ERROR
  423. }
  424. void drawComponentBuffer()
  425. {
  426. if (! isCoreProfile())
  427. glEnable (GL_TEXTURE_2D);
  428. #if JUCE_WINDOWS
  429. // some stupidly old drivers are missing this function, so try to at least avoid a crash here,
  430. // but if you hit this assertion you may want to have your own version check before using the
  431. // component rendering stuff on such old drivers.
  432. jassert (context.extensions.glActiveTexture != nullptr);
  433. if (context.extensions.glActiveTexture != nullptr)
  434. #endif
  435. {
  436. context.extensions.glActiveTexture (GL_TEXTURE0);
  437. }
  438. glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID());
  439. bindVertexArray();
  440. const Rectangle<int> cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight());
  441. context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight(), false);
  442. glBindTexture (GL_TEXTURE_2D, 0);
  443. JUCE_CHECK_OPENGL_ERROR
  444. }
  445. void paintOwner (LowLevelGraphicsContext& llgc)
  446. {
  447. Graphics g (llgc);
  448. #if JUCE_ENABLE_REPAINT_DEBUGGING
  449. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  450. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  451. #endif
  452. {
  453. g.saveState();
  454. }
  455. #endif
  456. JUCE_TRY
  457. {
  458. component.paintEntireComponent (g, false);
  459. }
  460. JUCE_CATCH_EXCEPTION
  461. #if JUCE_ENABLE_REPAINT_DEBUGGING
  462. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  463. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  464. #endif
  465. {
  466. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  467. // clearly when things are being repainted.
  468. g.restoreState();
  469. static Random rng;
  470. g.fillAll (Colour ((uint8) rng.nextInt (255),
  471. (uint8) rng.nextInt (255),
  472. (uint8) rng.nextInt (255),
  473. (uint8) 0x50));
  474. }
  475. #endif
  476. }
  477. void handleResize()
  478. {
  479. updateViewportSize();
  480. #if JUCE_MAC
  481. if (isFlagSet (state, StateFlags::initialised))
  482. {
  483. [nativeContext->view update];
  484. // We're already on the message thread, no need to lock it again.
  485. MessageManager::Lock mml;
  486. renderFrame (mml);
  487. }
  488. #endif
  489. }
  490. //==============================================================================
  491. InitResult initialiseOnThread()
  492. {
  493. // On android, this can get called twice, so drop any previous state.
  494. associatedObjectNames.clear();
  495. associatedObjects.clear();
  496. cachedImageFrameBuffer.release();
  497. context.makeActive();
  498. if (const auto nativeResult = nativeContext->initialiseOnRenderThread (context); nativeResult != InitResult::success)
  499. return nativeResult;
  500. #if JUCE_ANDROID
  501. // On android the context may be created in initialiseOnRenderThread
  502. // and we therefore need to call makeActive again
  503. context.makeActive();
  504. #endif
  505. gl::loadFunctions();
  506. if (shouldUseCustomVAO())
  507. {
  508. context.extensions.glGenVertexArrays (1, &vertexArrayObject);
  509. bindVertexArray();
  510. }
  511. #if JUCE_DEBUG
  512. if (getOpenGLVersion() >= Version { 4, 3 } && glDebugMessageCallback != nullptr)
  513. {
  514. glEnable (GL_DEBUG_OUTPUT);
  515. glEnable (GL_DEBUG_OUTPUT_SYNCHRONOUS);
  516. glDebugMessageCallback ([] (GLenum, GLenum type, GLuint, GLenum severity, GLsizei, const GLchar* message, const void*)
  517. {
  518. // This may reiterate issues that are also flagged by JUCE_CHECK_OPENGL_ERROR.
  519. // The advantage of this callback is that it will catch *all* errors, even if we
  520. // forget to check manually.
  521. DBG ("OpenGL DBG message: " << message);
  522. jassertquiet (type != GL_DEBUG_TYPE_ERROR && severity != GL_DEBUG_SEVERITY_HIGH);
  523. }, nullptr);
  524. }
  525. #endif
  526. const auto currentViewportArea = areaAndScale.get().area;
  527. glViewport (0, 0, currentViewportArea.getWidth(), currentViewportArea.getHeight());
  528. nativeContext->setSwapInterval (1);
  529. #if ! JUCE_OPENGL_ES
  530. JUCE_CHECK_OPENGL_ERROR
  531. shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0;
  532. clearGLError();
  533. #endif
  534. textureNpotSupported = contextHasTextureNpotFeature();
  535. if (context.renderer != nullptr)
  536. context.renderer->newOpenGLContextCreated();
  537. return InitResult::success;
  538. }
  539. bool isCoreProfile() const
  540. {
  541. #if JUCE_OPENGL_ES
  542. return true;
  543. #else
  544. clearGLError();
  545. GLint mask = 0;
  546. glGetIntegerv (GL_CONTEXT_PROFILE_MASK, &mask);
  547. // The context isn't aware of the profile mask, so it pre-dates the core profile
  548. if (glGetError() == GL_INVALID_ENUM)
  549. return false;
  550. // Also assumes a compatibility profile if the mask is completely empty for some reason
  551. return (mask & (GLint) GL_CONTEXT_CORE_PROFILE_BIT) != 0;
  552. #endif
  553. }
  554. /* Returns true if the context requires a non-zero vertex array object (VAO) to be bound.
  555. If the context is a compatibility context, we can just pretend that VAOs don't exist,
  556. and use the default VAO all the time instead. This provides a more consistent experience
  557. in user code, which might make calls (like glVertexPointer()) that only work when VAO 0 is
  558. bound in OpenGL 3.2+.
  559. */
  560. bool shouldUseCustomVAO() const
  561. {
  562. #if JUCE_OPENGL_ES
  563. return false;
  564. #else
  565. return isCoreProfile();
  566. #endif
  567. }
  568. //==============================================================================
  569. struct BlockingWorker : public OpenGLContext::AsyncWorker
  570. {
  571. BlockingWorker (OpenGLContext::AsyncWorker::Ptr && workerToUse)
  572. : originalWorker (std::move (workerToUse))
  573. {}
  574. void operator() (OpenGLContext& calleeContext)
  575. {
  576. if (originalWorker != nullptr)
  577. (*originalWorker) (calleeContext);
  578. finishedSignal.signal();
  579. }
  580. void block() noexcept { finishedSignal.wait(); }
  581. OpenGLContext::AsyncWorker::Ptr originalWorker;
  582. WaitableEvent finishedSignal;
  583. };
  584. void doWorkWhileWaitingForLock (ScopedContextActivator& contextActivator)
  585. {
  586. while (const auto work = workQueue.removeAndReturn (0))
  587. {
  588. if (renderThread->isListChanging() || ! contextActivator.activate (context))
  589. break;
  590. NativeContext::Locker locker (*nativeContext);
  591. (*work) (context);
  592. clearGLError();
  593. }
  594. }
  595. void execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock)
  596. {
  597. if (! isFlagSet (state, StateFlags::pendingDestruction))
  598. {
  599. if (shouldBlock)
  600. {
  601. auto blocker = new BlockingWorker (std::move (workerToUse));
  602. OpenGLContext::AsyncWorker::Ptr worker (*blocker);
  603. workQueue.add (worker);
  604. renderThread->abortLock();
  605. context.triggerRepaint();
  606. blocker->block();
  607. }
  608. else
  609. {
  610. workQueue.add (std::move (workerToUse));
  611. renderThread->abortLock();
  612. context.triggerRepaint();
  613. }
  614. }
  615. else
  616. {
  617. jassertfalse; // you called execute AFTER you detached your OpenGLContext
  618. }
  619. }
  620. //==============================================================================
  621. static CachedImage* get (Component& c) noexcept
  622. {
  623. return dynamic_cast<CachedImage*> (c.getCachedComponentImage());
  624. }
  625. class RenderThread
  626. {
  627. public:
  628. RenderThread() = default;
  629. ~RenderThread()
  630. {
  631. flags.setDestructing();
  632. thread.join();
  633. }
  634. void add (CachedImage* x)
  635. {
  636. const std::scoped_lock lock { listMutex };
  637. images.push_back (x);
  638. }
  639. void remove (CachedImage* x)
  640. {
  641. JUCE_ASSERT_MESSAGE_THREAD;
  642. flags.setSafe (false);
  643. abortLock();
  644. {
  645. const std::scoped_lock lock { callbackMutex, listMutex };
  646. images.remove (x);
  647. }
  648. flags.setSafe (true);
  649. }
  650. bool contains (CachedImage* x)
  651. {
  652. const std::scoped_lock lock { listMutex };
  653. return std::find (images.cbegin(), images.cend(), x) != images.cend();
  654. }
  655. void triggerRepaint() { flags.setRenderRequested(); }
  656. void abortLock() { messageManagerLock.abort(); }
  657. bool isListChanging() { return ! flags.isSafe(); }
  658. private:
  659. RenderStatus renderAll()
  660. {
  661. auto result = RenderStatus::noWork;
  662. const std::scoped_lock lock { callbackMutex, listMutex };
  663. for (auto* x : images)
  664. {
  665. listMutex.unlock();
  666. const ScopeGuard scope { [&] { listMutex.lock(); } };
  667. const auto status = x->renderFrame (messageManagerLock);
  668. switch (status)
  669. {
  670. case RenderStatus::noWork: break;
  671. case RenderStatus::nominal: result = RenderStatus::nominal; break;
  672. case RenderStatus::messageThreadAborted: return RenderStatus::messageThreadAborted;
  673. }
  674. }
  675. return result;
  676. }
  677. /* Allows the main thread to communicate changes to the render thread.
  678. When the render thread needs to change in some way (asked to resume rendering,
  679. a renderer is added/removed, or the thread needs to stop prior to destruction),
  680. the main thread can set the appropriate flag on this structure. The render thread
  681. will call waitForWork() repeatedly, pausing when the render thread has no work to do,
  682. and resuming when requested by the main thread.
  683. */
  684. class Flags
  685. {
  686. public:
  687. void setDestructing() { update ([] (auto& f) { f |= destructorCalled; }); }
  688. void setRenderRequested() { update ([] (auto& f) { f |= renderRequested; }); }
  689. void setSafe (const bool safe)
  690. {
  691. update ([safe] (auto& f)
  692. {
  693. if (safe)
  694. f |= listSafe;
  695. else
  696. f &= ~listSafe;
  697. });
  698. }
  699. bool isSafe()
  700. {
  701. const std::scoped_lock lock { mutex };
  702. return (flags & listSafe) != 0;
  703. }
  704. /* Blocks until the 'safe' flag is set, and at least one other flag is set.
  705. After returning, the renderRequested flag will be unset.
  706. Returns true if rendering should continue.
  707. */
  708. bool waitForWork (bool requestRender)
  709. {
  710. std::unique_lock lock { mutex };
  711. flags |= (requestRender ? renderRequested : 0);
  712. condvar.wait (lock, [this] { return flags > listSafe; });
  713. flags &= ~renderRequested;
  714. return ((flags & destructorCalled) == 0);
  715. }
  716. private:
  717. template <typename Fn>
  718. void update (Fn fn)
  719. {
  720. {
  721. const std::scoped_lock lock { mutex };
  722. fn (flags);
  723. }
  724. condvar.notify_one();
  725. }
  726. enum
  727. {
  728. renderRequested = 1 << 0,
  729. destructorCalled = 1 << 1,
  730. listSafe = 1 << 2
  731. };
  732. std::mutex mutex;
  733. std::condition_variable condvar;
  734. int flags = listSafe;
  735. };
  736. MessageManager::Lock messageManagerLock;
  737. std::mutex listMutex, callbackMutex;
  738. std::list<CachedImage*> images;
  739. Flags flags;
  740. std::thread thread { [this]
  741. {
  742. Thread::setCurrentThreadName ("OpenGL Renderer");
  743. while (flags.waitForWork (renderAll() != RenderStatus::noWork)) {}
  744. } };
  745. };
  746. void refreshDisplayLinkConnection()
  747. {
  748. #if JUCE_MAC
  749. if (context.continuousRepaint)
  750. {
  751. connection.emplace (sharedDisplayLinks->registerFactory ([this] (CGDirectDisplayID display)
  752. {
  753. return [this, display]
  754. {
  755. if (display == lastDisplay)
  756. triggerRepaint();
  757. };
  758. }));
  759. }
  760. else
  761. {
  762. connection.reset();
  763. }
  764. #endif
  765. }
  766. //==============================================================================
  767. class BufferSwapper : private AsyncUpdater
  768. {
  769. public:
  770. explicit BufferSwapper (CachedImage& img)
  771. : image (img) {}
  772. ~BufferSwapper() override
  773. {
  774. cancelPendingUpdate();
  775. }
  776. void swap()
  777. {
  778. static const auto swapBuffersOnMainThread = []
  779. {
  780. const auto os = SystemStats::getOperatingSystemType();
  781. if ((os & SystemStats::MacOSX) != 0)
  782. return (os != SystemStats::MacOSX && os < SystemStats::MacOSX_10_14);
  783. return false;
  784. }();
  785. if (swapBuffersOnMainThread && ! MessageManager::getInstance()->isThisTheMessageThread())
  786. triggerAsyncUpdate();
  787. else
  788. image.nativeContext->swapBuffers();
  789. }
  790. private:
  791. void handleAsyncUpdate() override
  792. {
  793. ScopedContextActivator activator;
  794. activator.activate (image.context);
  795. NativeContext::Locker locker (*image.nativeContext);
  796. image.nativeContext->swapBuffers();
  797. }
  798. CachedImage& image;
  799. };
  800. //==============================================================================
  801. friend class NativeContext;
  802. std::unique_ptr<NativeContext> nativeContext;
  803. OpenGLContext& context;
  804. Component& component;
  805. SharedResourcePointer<RenderThread> renderThread;
  806. OpenGLFrameBuffer cachedImageFrameBuffer;
  807. RectangleList<int> validArea;
  808. Rectangle<int> lastScreenBounds;
  809. AffineTransform transform;
  810. GLuint vertexArrayObject = 0;
  811. LockedAreaAndScale areaAndScale;
  812. StringArray associatedObjectNames;
  813. ReferenceCountedArray<ReferenceCountedObject> associatedObjects;
  814. WaitableEvent canPaintNowFlag, finishedPaintingFlag;
  815. #if JUCE_OPENGL_ES
  816. bool shadersAvailable = true;
  817. #else
  818. bool shadersAvailable = false;
  819. #endif
  820. bool textureNpotSupported = false;
  821. std::chrono::steady_clock::time_point lastMMLockReleaseTime{};
  822. BufferSwapper bufferSwapper { *this };
  823. #if JUCE_MAC
  824. NSView* getCurrentView() const
  825. {
  826. JUCE_ASSERT_MESSAGE_THREAD;
  827. if (auto* peer = component.getPeer())
  828. return static_cast<NSView*> (peer->getNativeHandle());
  829. return nullptr;
  830. }
  831. NSWindow* getCurrentWindow() const
  832. {
  833. JUCE_ASSERT_MESSAGE_THREAD;
  834. if (auto* view = getCurrentView())
  835. return [view window];
  836. return nullptr;
  837. }
  838. NSScreen* getCurrentScreen() const
  839. {
  840. JUCE_ASSERT_MESSAGE_THREAD;
  841. if (auto* window = getCurrentWindow())
  842. return [window screen];
  843. return nullptr;
  844. }
  845. void updateScreen()
  846. {
  847. const auto screen = getCurrentScreen();
  848. const auto display = ScopedDisplayLink::getDisplayIdForScreen (screen);
  849. if (lastDisplay.exchange (display) == display)
  850. return;
  851. const auto newRefreshPeriod = sharedDisplayLinks->getNominalVideoRefreshPeriodSForScreen (display);
  852. if (newRefreshPeriod != 0.0 && ! approximatelyEqual (std::exchange (refreshPeriod, newRefreshPeriod), newRefreshPeriod))
  853. nativeContext->setNominalVideoRefreshPeriodS (newRefreshPeriod);
  854. updateColourSpace();
  855. }
  856. void updateColourSpace()
  857. {
  858. if (auto* view = nativeContext->getNSView())
  859. if (auto* window = [view window])
  860. [window setColorSpace: [NSColorSpace sRGBColorSpace]];
  861. }
  862. std::atomic<CGDirectDisplayID> lastDisplay { 0 };
  863. double refreshPeriod = 0.0;
  864. FunctionNotificationCenterObserver observer { NSWindowDidChangeScreenNotification,
  865. getCurrentWindow(),
  866. [this] { updateScreen(); } };
  867. // Note: the NSViewComponentPeer also has a SharedResourcePointer<PerScreenDisplayLinks> to
  868. // avoid unnecessarily duplicating display-link threads.
  869. SharedResourcePointer<PerScreenDisplayLinks> sharedDisplayLinks;
  870. // On macOS, rather than letting swapBuffers block as appropriate, we use a display link
  871. // callback to mark the view as needing to repaint.
  872. std::optional<PerScreenDisplayLinks::Connection> connection;
  873. #endif
  874. enum StateFlags
  875. {
  876. pendingRender = 1 << 0,
  877. paintComponents = 1 << 1,
  878. pendingDestruction = 1 << 2,
  879. initialised = 1 << 3,
  880. // Flags that should retain their state after each frame
  881. persistent = initialised | pendingDestruction
  882. };
  883. std::atomic<int> state { 0 };
  884. ReferenceCountedArray<OpenGLContext::AsyncWorker, CriticalSection> workQueue;
  885. #if JUCE_IOS
  886. iOSBackgroundProcessCheck backgroundProcessCheck;
  887. #endif
  888. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
  889. };
  890. //==============================================================================
  891. class OpenGLContext::Attachment : public ComponentMovementWatcher,
  892. private Timer
  893. {
  894. public:
  895. Attachment (OpenGLContext& c, Component& comp)
  896. : ComponentMovementWatcher (&comp), context (c)
  897. {
  898. if (canBeAttached (comp))
  899. attach();
  900. }
  901. ~Attachment() override
  902. {
  903. detach();
  904. }
  905. void detach()
  906. {
  907. auto& comp = *getComponent();
  908. stop();
  909. comp.setCachedComponentImage (nullptr);
  910. context.nativeContext = nullptr;
  911. }
  912. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  913. {
  914. auto& comp = *getComponent();
  915. if (isAttached (comp) != canBeAttached (comp))
  916. componentVisibilityChanged();
  917. if (comp.getWidth() > 0 && comp.getHeight() > 0
  918. && context.nativeContext != nullptr)
  919. {
  920. if (auto* c = CachedImage::get (comp))
  921. c->handleResize();
  922. if (auto* peer = comp.getTopLevelComponent()->getPeer())
  923. context.nativeContext->updateWindowPosition (peer->getAreaCoveredBy (comp));
  924. }
  925. }
  926. using ComponentMovementWatcher::componentMovedOrResized;
  927. void componentPeerChanged() override
  928. {
  929. detach();
  930. componentVisibilityChanged();
  931. }
  932. void componentVisibilityChanged() override
  933. {
  934. auto& comp = *getComponent();
  935. if (canBeAttached (comp))
  936. {
  937. if (isAttached (comp))
  938. comp.repaint(); // (needed when windows are un-minimised)
  939. else
  940. attach();
  941. }
  942. else
  943. {
  944. detach();
  945. }
  946. }
  947. using ComponentMovementWatcher::componentVisibilityChanged;
  948. #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
  949. void componentBeingDeleted (Component& c) override
  950. {
  951. /* You must call detach() or delete your OpenGLContext to remove it
  952. from a component BEFORE deleting the component that it is using!
  953. */
  954. jassertfalse;
  955. ComponentMovementWatcher::componentBeingDeleted (c);
  956. }
  957. #endif
  958. private:
  959. OpenGLContext& context;
  960. bool canBeAttached (const Component& comp) noexcept
  961. {
  962. return (! context.overrideCanAttach) && comp.getWidth() > 0 && comp.getHeight() > 0 && isShowingOrMinimised (comp);
  963. }
  964. static bool isShowingOrMinimised (const Component& c)
  965. {
  966. if (! c.isVisible())
  967. return false;
  968. if (auto* p = c.getParentComponent())
  969. return isShowingOrMinimised (*p);
  970. return c.getPeer() != nullptr;
  971. }
  972. static bool isAttached (const Component& comp) noexcept
  973. {
  974. return comp.getCachedComponentImage() != nullptr;
  975. }
  976. void attach()
  977. {
  978. auto& comp = *getComponent();
  979. auto* newCachedImage = new CachedImage (context, comp,
  980. context.openGLPixelFormat,
  981. context.contextToShareWith);
  982. comp.setCachedComponentImage (newCachedImage);
  983. start();
  984. }
  985. void stop()
  986. {
  987. stopTimer();
  988. auto& comp = *getComponent();
  989. #if JUCE_MAC
  990. [[(NSView*) comp.getWindowHandle() window] disableScreenUpdatesUntilFlush];
  991. #endif
  992. if (auto* oldCachedImage = CachedImage::get (comp))
  993. oldCachedImage->stop(); // (must stop this before detaching it from the component)
  994. }
  995. void start()
  996. {
  997. auto& comp = *getComponent();
  998. if (auto* cachedImage = CachedImage::get (comp))
  999. {
  1000. cachedImage->start(); // (must wait until this is attached before starting its thread)
  1001. cachedImage->updateViewportSize();
  1002. startTimer (400);
  1003. }
  1004. }
  1005. void timerCallback() override
  1006. {
  1007. if (auto* cachedImage = CachedImage::get (*getComponent()))
  1008. cachedImage->checkViewportBounds();
  1009. }
  1010. };
  1011. //==============================================================================
  1012. OpenGLContext::OpenGLContext()
  1013. {
  1014. }
  1015. OpenGLContext::~OpenGLContext()
  1016. {
  1017. detach();
  1018. }
  1019. void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept
  1020. {
  1021. // This method must not be called when the context has already been attached!
  1022. // Call it before attaching your context, or use detach() first, before calling this!
  1023. jassert (nativeContext == nullptr);
  1024. renderer = rendererToUse;
  1025. }
  1026. void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept
  1027. {
  1028. // This method must not be called when the context has already been attached!
  1029. // Call it before attaching your context, or use detach() first, before calling this!
  1030. jassert (nativeContext == nullptr);
  1031. renderComponents = shouldPaintComponent;
  1032. }
  1033. void OpenGLContext::setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept
  1034. {
  1035. continuousRepaint = shouldContinuouslyRepaint;
  1036. #if JUCE_MAC
  1037. if (auto* component = getTargetComponent())
  1038. {
  1039. detach();
  1040. attachment.reset (new Attachment (*this, *component));
  1041. }
  1042. if (auto* cachedImage = getCachedImage())
  1043. cachedImage->refreshDisplayLinkConnection();
  1044. #endif
  1045. triggerRepaint();
  1046. }
  1047. void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept
  1048. {
  1049. // This method must not be called when the context has already been attached!
  1050. // Call it before attaching your context, or use detach() first, before calling this!
  1051. jassert (nativeContext == nullptr);
  1052. openGLPixelFormat = preferredPixelFormat;
  1053. }
  1054. void OpenGLContext::setTextureMagnificationFilter (OpenGLContext::TextureMagnificationFilter magFilterMode) noexcept
  1055. {
  1056. texMagFilter = magFilterMode;
  1057. }
  1058. void OpenGLContext::setNativeSharedContext (void* nativeContextToShareWith) noexcept
  1059. {
  1060. // This method must not be called when the context has already been attached!
  1061. // Call it before attaching your context, or use detach() first, before calling this!
  1062. jassert (nativeContext == nullptr);
  1063. contextToShareWith = nativeContextToShareWith;
  1064. }
  1065. void OpenGLContext::setMultisamplingEnabled (bool b) noexcept
  1066. {
  1067. // This method must not be called when the context has already been attached!
  1068. // Call it before attaching your context, or use detach() first, before calling this!
  1069. jassert (nativeContext == nullptr);
  1070. useMultisampling = b;
  1071. }
  1072. void OpenGLContext::setOpenGLVersionRequired (OpenGLVersion v) noexcept
  1073. {
  1074. versionRequired = v;
  1075. }
  1076. void OpenGLContext::attachTo (Component& component)
  1077. {
  1078. component.repaint();
  1079. if (getTargetComponent() != &component)
  1080. {
  1081. detach();
  1082. attachment.reset (new Attachment (*this, component));
  1083. }
  1084. }
  1085. void OpenGLContext::detach()
  1086. {
  1087. if (auto* a = attachment.get())
  1088. {
  1089. a->detach(); // must detach before nulling our pointer
  1090. attachment.reset();
  1091. }
  1092. nativeContext = nullptr;
  1093. }
  1094. bool OpenGLContext::isAttached() const noexcept
  1095. {
  1096. return nativeContext != nullptr;
  1097. }
  1098. Component* OpenGLContext::getTargetComponent() const noexcept
  1099. {
  1100. return attachment != nullptr ? attachment->getComponent() : nullptr;
  1101. }
  1102. OpenGLContext* OpenGLContext::getContextAttachedTo (Component& c) noexcept
  1103. {
  1104. if (auto* ci = CachedImage::get (c))
  1105. return &(ci->context);
  1106. return nullptr;
  1107. }
  1108. static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext;
  1109. OpenGLContext* OpenGLContext::getCurrentContext()
  1110. {
  1111. return currentThreadActiveContext.get();
  1112. }
  1113. bool OpenGLContext::makeActive() const noexcept
  1114. {
  1115. auto& current = currentThreadActiveContext.get();
  1116. if (nativeContext != nullptr && nativeContext->makeActive())
  1117. {
  1118. current = const_cast<OpenGLContext*> (this);
  1119. return true;
  1120. }
  1121. current = nullptr;
  1122. return false;
  1123. }
  1124. bool OpenGLContext::isActive() const noexcept
  1125. {
  1126. return nativeContext != nullptr && nativeContext->isActive();
  1127. }
  1128. void OpenGLContext::deactivateCurrentContext()
  1129. {
  1130. NativeContext::deactivateCurrentContext();
  1131. currentThreadActiveContext.get() = nullptr;
  1132. }
  1133. void OpenGLContext::triggerRepaint()
  1134. {
  1135. if (auto* cachedImage = getCachedImage())
  1136. cachedImage->triggerRepaint();
  1137. }
  1138. void OpenGLContext::swapBuffers()
  1139. {
  1140. if (nativeContext != nullptr)
  1141. nativeContext->swapBuffers();
  1142. }
  1143. unsigned int OpenGLContext::getFrameBufferID() const noexcept
  1144. {
  1145. return nativeContext != nullptr ? nativeContext->getFrameBufferID() : 0;
  1146. }
  1147. bool OpenGLContext::setSwapInterval (int numFramesPerSwap)
  1148. {
  1149. return nativeContext != nullptr && nativeContext->setSwapInterval (numFramesPerSwap);
  1150. }
  1151. int OpenGLContext::getSwapInterval() const
  1152. {
  1153. return nativeContext != nullptr ? nativeContext->getSwapInterval() : 0;
  1154. }
  1155. void* OpenGLContext::getRawContext() const noexcept
  1156. {
  1157. return nativeContext != nullptr ? nativeContext->getRawContext() : nullptr;
  1158. }
  1159. bool OpenGLContext::isCoreProfile() const
  1160. {
  1161. auto* c = getCachedImage();
  1162. return c != nullptr && c->isCoreProfile();
  1163. }
  1164. OpenGLContext::CachedImage* OpenGLContext::getCachedImage() const noexcept
  1165. {
  1166. if (auto* comp = getTargetComponent())
  1167. return CachedImage::get (*comp);
  1168. return nullptr;
  1169. }
  1170. bool OpenGLContext::areShadersAvailable() const
  1171. {
  1172. auto* c = getCachedImage();
  1173. return c != nullptr && c->shadersAvailable;
  1174. }
  1175. bool OpenGLContext::isTextureNpotSupported() const
  1176. {
  1177. auto* c = getCachedImage();
  1178. return c != nullptr && c->textureNpotSupported;
  1179. }
  1180. ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const
  1181. {
  1182. jassert (name != nullptr);
  1183. auto* c = getCachedImage();
  1184. // This method must only be called from an openGL rendering callback.
  1185. jassert (c != nullptr && nativeContext != nullptr);
  1186. jassert (getCurrentContext() != nullptr);
  1187. auto index = c->associatedObjectNames.indexOf (name);
  1188. return index >= 0 ? c->associatedObjects.getUnchecked (index).get() : nullptr;
  1189. }
  1190. void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObject* newObject)
  1191. {
  1192. jassert (name != nullptr);
  1193. if (auto* c = getCachedImage())
  1194. {
  1195. // This method must only be called from an openGL rendering callback.
  1196. jassert (nativeContext != nullptr);
  1197. jassert (getCurrentContext() != nullptr);
  1198. const int index = c->associatedObjectNames.indexOf (name);
  1199. if (index >= 0)
  1200. {
  1201. if (newObject != nullptr)
  1202. {
  1203. c->associatedObjects.set (index, newObject);
  1204. }
  1205. else
  1206. {
  1207. c->associatedObjectNames.remove (index);
  1208. c->associatedObjects.remove (index);
  1209. }
  1210. }
  1211. else if (newObject != nullptr)
  1212. {
  1213. c->associatedObjectNames.add (name);
  1214. c->associatedObjects.add (newObject);
  1215. }
  1216. }
  1217. }
  1218. void OpenGLContext::setImageCacheSize (size_t newSize) noexcept { imageCacheMaxSize = newSize; }
  1219. size_t OpenGLContext::getImageCacheSize() const noexcept { return imageCacheMaxSize; }
  1220. void OpenGLContext::execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock)
  1221. {
  1222. if (auto* c = getCachedImage())
  1223. c->execute (std::move (workerToUse), shouldBlock);
  1224. else
  1225. jassertfalse; // You must have attached the context to a component
  1226. }
  1227. //==============================================================================
  1228. struct DepthTestDisabler
  1229. {
  1230. DepthTestDisabler() noexcept
  1231. {
  1232. glGetBooleanv (GL_DEPTH_TEST, &wasEnabled);
  1233. if (wasEnabled)
  1234. glDisable (GL_DEPTH_TEST);
  1235. }
  1236. ~DepthTestDisabler() noexcept
  1237. {
  1238. if (wasEnabled)
  1239. glEnable (GL_DEPTH_TEST);
  1240. }
  1241. GLboolean wasEnabled;
  1242. };
  1243. //==============================================================================
  1244. void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
  1245. const Rectangle<int>& anchorPosAndTextureSize,
  1246. const int contextWidth, const int contextHeight,
  1247. bool flippedVertically)
  1248. {
  1249. if (contextWidth <= 0 || contextHeight <= 0)
  1250. return;
  1251. JUCE_CHECK_OPENGL_ERROR
  1252. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  1253. glEnable (GL_BLEND);
  1254. DepthTestDisabler depthDisabler;
  1255. if (areShadersAvailable())
  1256. {
  1257. struct OverlayShaderProgram : public ReferenceCountedObject
  1258. {
  1259. OverlayShaderProgram (OpenGLContext& context)
  1260. : program (context), params (program)
  1261. {}
  1262. static const OverlayShaderProgram& select (OpenGLContext& context)
  1263. {
  1264. static const char programValueID[] = "juceGLComponentOverlayShader";
  1265. OverlayShaderProgram* program = static_cast<OverlayShaderProgram*> (context.getAssociatedObject (programValueID));
  1266. if (program == nullptr)
  1267. {
  1268. program = new OverlayShaderProgram (context);
  1269. context.setAssociatedObject (programValueID, program);
  1270. }
  1271. program->program.use();
  1272. return *program;
  1273. }
  1274. struct BuiltProgram : public OpenGLShaderProgram
  1275. {
  1276. explicit BuiltProgram (OpenGLContext& ctx)
  1277. : OpenGLShaderProgram (ctx)
  1278. {
  1279. addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (
  1280. "attribute " JUCE_HIGHP " vec2 position;"
  1281. "uniform " JUCE_HIGHP " vec2 screenSize;"
  1282. "uniform " JUCE_HIGHP " float textureBounds[4];"
  1283. "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
  1284. "varying " JUCE_HIGHP " vec2 texturePos;"
  1285. "void main()"
  1286. "{"
  1287. JUCE_HIGHP " vec2 scaled = position / (0.5 * screenSize.xy);"
  1288. "gl_Position = vec4 (scaled.x - 1.0, 1.0 - scaled.y, 0, 1.0);"
  1289. "texturePos = (position - vec2 (textureBounds[0], textureBounds[1])) / vec2 (textureBounds[2], textureBounds[3]);"
  1290. "texturePos = vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y);"
  1291. "}"));
  1292. addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (
  1293. "uniform sampler2D imageTexture;"
  1294. "varying " JUCE_HIGHP " vec2 texturePos;"
  1295. "void main()"
  1296. "{"
  1297. "gl_FragColor = texture2D (imageTexture, texturePos);"
  1298. "}"));
  1299. link();
  1300. }
  1301. };
  1302. struct Params
  1303. {
  1304. Params (OpenGLShaderProgram& prog)
  1305. : positionAttribute (prog, "position"),
  1306. screenSize (prog, "screenSize"),
  1307. imageTexture (prog, "imageTexture"),
  1308. textureBounds (prog, "textureBounds"),
  1309. vOffsetAndScale (prog, "vOffsetAndScale")
  1310. {}
  1311. void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flipVertically) const
  1312. {
  1313. const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
  1314. textureBounds.set (m, 4);
  1315. imageTexture.set (0);
  1316. screenSize.set (targetWidth, targetHeight);
  1317. vOffsetAndScale.set (flipVertically ? 0.0f : 1.0f,
  1318. flipVertically ? 1.0f : -1.0f);
  1319. }
  1320. OpenGLShaderProgram::Attribute positionAttribute;
  1321. OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
  1322. };
  1323. BuiltProgram program;
  1324. Params params;
  1325. };
  1326. auto left = (GLshort) targetClipArea.getX();
  1327. auto top = (GLshort) targetClipArea.getY();
  1328. auto right = (GLshort) targetClipArea.getRight();
  1329. auto bottom = (GLshort) targetClipArea.getBottom();
  1330. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  1331. auto& program = OverlayShaderProgram::select (*this);
  1332. program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
  1333. GLuint vertexBuffer = 0;
  1334. extensions.glGenBuffers (1, &vertexBuffer);
  1335. extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  1336. extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
  1337. auto index = (GLuint) program.params.positionAttribute.attributeID;
  1338. extensions.glVertexAttribPointer (index, 2, GL_SHORT, GL_FALSE, 4, nullptr);
  1339. extensions.glEnableVertexAttribArray (index);
  1340. JUCE_CHECK_OPENGL_ERROR
  1341. if (extensions.glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
  1342. {
  1343. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  1344. extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  1345. extensions.glUseProgram (0);
  1346. extensions.glDisableVertexAttribArray (index);
  1347. extensions.glDeleteBuffers (1, &vertexBuffer);
  1348. }
  1349. else
  1350. {
  1351. clearGLError();
  1352. }
  1353. }
  1354. else
  1355. {
  1356. jassert (attachment == nullptr); // Running on an old graphics card!
  1357. }
  1358. JUCE_CHECK_OPENGL_ERROR
  1359. }
  1360. #if JUCE_ANDROID
  1361. void OpenGLContext::NativeContext::surfaceCreated (LocalRef<jobject>)
  1362. {
  1363. {
  1364. const std::lock_guard lock { nativeHandleMutex };
  1365. jassert (hasInitialised);
  1366. // has the context already attached?
  1367. jassert (surface.get() == EGL_NO_SURFACE && context.get() == EGL_NO_CONTEXT);
  1368. const auto window = getNativeWindow();
  1369. if (window == nullptr)
  1370. {
  1371. // failed to get a pointer to the native window so bail out
  1372. jassertfalse;
  1373. return;
  1374. }
  1375. // create the surface
  1376. surface.reset (eglCreateWindowSurface (display, config, window.get(), nullptr));
  1377. jassert (surface.get() != EGL_NO_SURFACE);
  1378. // create the OpenGL context
  1379. EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
  1380. context.reset (eglCreateContext (display, config, EGL_NO_CONTEXT, contextAttribs));
  1381. jassert (context.get() != EGL_NO_CONTEXT);
  1382. }
  1383. if (auto* cached = CachedImage::get (component))
  1384. {
  1385. cached->resume();
  1386. cached->triggerRepaint();
  1387. }
  1388. }
  1389. void OpenGLContext::NativeContext::surfaceDestroyed (LocalRef<jobject>)
  1390. {
  1391. if (auto* cached = CachedImage::get (component))
  1392. cached->pause();
  1393. {
  1394. const std::lock_guard lock { nativeHandleMutex };
  1395. context.reset (EGL_NO_CONTEXT);
  1396. surface.reset (EGL_NO_SURFACE);
  1397. }
  1398. }
  1399. #endif
  1400. } // namespace juce