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.

982 lines
31KB

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