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.

977 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. const Rectangle<int> cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight());
  236. context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight(), false);
  237. glBindTexture (GL_TEXTURE_2D, 0);
  238. JUCE_CHECK_OPENGL_ERROR
  239. }
  240. void paintOwner (LowLevelGraphicsContext& llgc)
  241. {
  242. Graphics g (llgc);
  243. #if JUCE_ENABLE_REPAINT_DEBUGGING
  244. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  245. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  246. #endif
  247. {
  248. g.saveState();
  249. }
  250. #endif
  251. JUCE_TRY
  252. {
  253. component.paintEntireComponent (g, false);
  254. }
  255. JUCE_CATCH_EXCEPTION
  256. #if JUCE_ENABLE_REPAINT_DEBUGGING
  257. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  258. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  259. #endif
  260. {
  261. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  262. // clearly when things are being repainted.
  263. g.restoreState();
  264. static Random rng;
  265. g.fillAll (Colour ((uint8) rng.nextInt (255),
  266. (uint8) rng.nextInt (255),
  267. (uint8) rng.nextInt (255),
  268. (uint8) 0x50));
  269. }
  270. #endif
  271. }
  272. void handleResize()
  273. {
  274. updateViewportSize (true);
  275. #if JUCE_MAC
  276. if (hasInitialised)
  277. {
  278. [nativeContext->view update];
  279. renderFrame();
  280. }
  281. #endif
  282. }
  283. //==============================================================================
  284. #if JUCE_OPENGL_CREATE_JUCE_RENDER_THREAD
  285. void run() override
  286. {
  287. {
  288. // Allow the message thread to finish setting-up the context before using it..
  289. MessageManagerLock mml (this);
  290. if (! mml.lockWasGained())
  291. return;
  292. }
  293. initialiseOnThread();
  294. hasInitialised = true;
  295. while (! threadShouldExit())
  296. {
  297. #if JUCE_IOS
  298. // NB: on iOS, all GL calls will crash when the app is running
  299. // in the background..
  300. if (! Process::isForegroundProcess())
  301. {
  302. wait (500);
  303. continue;
  304. }
  305. #endif
  306. if (! renderFrame())
  307. wait (5); // failed to render, so avoid a tight fail-loop.
  308. else if (! context.continuousRepaint)
  309. wait (-1);
  310. }
  311. shutdownOnThread();
  312. }
  313. #endif
  314. void initialiseOnThread()
  315. {
  316. // On android, this can get called twice, so drop any previous state..
  317. associatedObjectNames.clear();
  318. associatedObjects.clear();
  319. cachedImageFrameBuffer.release();
  320. context.makeActive();
  321. nativeContext->initialiseOnRenderThread (context);
  322. #if JUCE_OPENGL3
  323. if (OpenGLShaderProgram::getLanguageVersion() > 1.2)
  324. {
  325. glGenVertexArrays (1, &vertexArrayObject);
  326. glBindVertexArray (vertexArrayObject);
  327. }
  328. #endif
  329. glViewport (0, 0, component.getWidth(), component.getHeight());
  330. context.extensions.initialise();
  331. nativeContext->setSwapInterval (1);
  332. #if ! JUCE_OPENGL_ES
  333. shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0;
  334. #endif
  335. if (context.renderer != nullptr)
  336. context.renderer->newOpenGLContextCreated();
  337. }
  338. void shutdownOnThread()
  339. {
  340. if (context.renderer != nullptr)
  341. context.renderer->openGLContextClosing();
  342. #if JUCE_OPENGL3
  343. if (vertexArrayObject != 0)
  344. glDeleteVertexArrays (1, &vertexArrayObject);
  345. #endif
  346. cachedImageFrameBuffer.release();
  347. nativeContext->shutdownOnRenderThread();
  348. associatedObjectNames.clear();
  349. associatedObjects.clear();
  350. }
  351. //==============================================================================
  352. static CachedImage* get (Component& c) noexcept
  353. {
  354. return dynamic_cast<CachedImage*> (c.getCachedComponentImage());
  355. }
  356. //==============================================================================
  357. ScopedPointer<NativeContext> nativeContext;
  358. OpenGLContext& context;
  359. Component& component;
  360. OpenGLFrameBuffer cachedImageFrameBuffer;
  361. RectangleList<int> validArea;
  362. Rectangle<int> viewportArea, lastScreenBounds;
  363. double scale;
  364. #if JUCE_OPENGL3
  365. GLuint vertexArrayObject;
  366. #endif
  367. StringArray associatedObjectNames;
  368. ReferenceCountedArray<ReferenceCountedObject> associatedObjects;
  369. WaitableEvent canPaintNowFlag, finishedPaintingFlag;
  370. bool shadersAvailable, hasInitialised;
  371. Atomic<int> needsUpdate;
  372. uint32 lastMMLockReleaseTime;
  373. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
  374. };
  375. //==============================================================================
  376. class OpenGLContext::Attachment : public ComponentMovementWatcher,
  377. private Timer
  378. {
  379. public:
  380. Attachment (OpenGLContext& c, Component& comp)
  381. : ComponentMovementWatcher (&comp), context (c)
  382. {
  383. if (canBeAttached (comp))
  384. attach();
  385. }
  386. ~Attachment()
  387. {
  388. detach();
  389. }
  390. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  391. {
  392. Component& comp = *getComponent();
  393. if (isAttached (comp) != canBeAttached (comp))
  394. componentVisibilityChanged();
  395. if (comp.getWidth() > 0 && comp.getHeight() > 0
  396. && context.nativeContext != nullptr)
  397. {
  398. if (CachedImage* const c = CachedImage::get (comp))
  399. c->handleResize();
  400. if (ComponentPeer* peer = comp.getTopLevelComponent()->getPeer())
  401. context.nativeContext->updateWindowPosition (peer->getAreaCoveredBy (comp));
  402. }
  403. }
  404. void componentPeerChanged() override
  405. {
  406. detach();
  407. componentVisibilityChanged();
  408. }
  409. void componentVisibilityChanged() override
  410. {
  411. Component& comp = *getComponent();
  412. if (canBeAttached (comp))
  413. {
  414. if (isAttached (comp))
  415. comp.repaint(); // (needed when windows are un-minimised)
  416. else
  417. attach();
  418. }
  419. else
  420. {
  421. detach();
  422. }
  423. }
  424. #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
  425. void componentBeingDeleted (Component& c) override
  426. {
  427. /* You must call detach() or delete your OpenGLContext to remove it
  428. from a component BEFORE deleting the component that it is using!
  429. */
  430. jassertfalse;
  431. ComponentMovementWatcher::componentBeingDeleted (c);
  432. }
  433. #endif
  434. private:
  435. OpenGLContext& context;
  436. static bool canBeAttached (const Component& comp) noexcept
  437. {
  438. return comp.getWidth() > 0 && comp.getHeight() > 0 && isShowingOrMinimised (comp);
  439. }
  440. static bool isShowingOrMinimised (const Component& c)
  441. {
  442. if (! c.isVisible())
  443. return false;
  444. if (Component* p = c.getParentComponent())
  445. return isShowingOrMinimised (*p);
  446. return c.getPeer() != nullptr;
  447. }
  448. static bool isAttached (const Component& comp) noexcept
  449. {
  450. return comp.getCachedComponentImage() != nullptr;
  451. }
  452. void attach()
  453. {
  454. Component& comp = *getComponent();
  455. CachedImage* const newCachedImage = new CachedImage (context, comp,
  456. context.pixelFormat,
  457. context.contextToShareWith);
  458. comp.setCachedComponentImage (newCachedImage);
  459. newCachedImage->start(); // (must wait until this is attached before starting its thread)
  460. newCachedImage->updateViewportSize (true);
  461. startTimer (400);
  462. }
  463. void detach()
  464. {
  465. stopTimer();
  466. Component& comp = *getComponent();
  467. #if JUCE_MAC
  468. [[(NSView*) comp.getWindowHandle() window] disableScreenUpdatesUntilFlush];
  469. #endif
  470. if (CachedImage* const oldCachedImage = CachedImage::get (comp))
  471. oldCachedImage->stop(); // (must stop this before detaching it from the component)
  472. comp.setCachedComponentImage (nullptr);
  473. context.nativeContext = nullptr;
  474. }
  475. void timerCallback() override
  476. {
  477. if (CachedImage* const cachedImage = CachedImage::get (*getComponent()))
  478. cachedImage->checkViewportBounds();
  479. }
  480. };
  481. //==============================================================================
  482. OpenGLContext::OpenGLContext()
  483. : nativeContext (nullptr), renderer (nullptr), currentRenderScale (1.0),
  484. contextToShareWith (nullptr), versionRequired (OpenGLContext::defaultGLVersion),
  485. imageCacheMaxSize (8 * 1024 * 1024),
  486. renderComponents (true), useMultisampling (false), continuousRepaint (false)
  487. {
  488. }
  489. OpenGLContext::~OpenGLContext()
  490. {
  491. detach();
  492. }
  493. void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept
  494. {
  495. // This method must not be called when the context has already been attached!
  496. // Call it before attaching your context, or use detach() first, before calling this!
  497. jassert (nativeContext == nullptr);
  498. renderer = rendererToUse;
  499. }
  500. void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept
  501. {
  502. // This method must not be called when the context has already been attached!
  503. // Call it before attaching your context, or use detach() first, before calling this!
  504. jassert (nativeContext == nullptr);
  505. renderComponents = shouldPaintComponent;
  506. }
  507. void OpenGLContext::setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept
  508. {
  509. continuousRepaint = shouldContinuouslyRepaint;
  510. triggerRepaint();
  511. }
  512. void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept
  513. {
  514. // This method must not be called when the context has already been attached!
  515. // Call it before attaching your context, or use detach() first, before calling this!
  516. jassert (nativeContext == nullptr);
  517. pixelFormat = preferredPixelFormat;
  518. }
  519. void OpenGLContext::setNativeSharedContext (void* nativeContextToShareWith) noexcept
  520. {
  521. // This method must not be called when the context has already been attached!
  522. // Call it before attaching your context, or use detach() first, before calling this!
  523. jassert (nativeContext == nullptr);
  524. contextToShareWith = nativeContextToShareWith;
  525. }
  526. void OpenGLContext::setMultisamplingEnabled (bool b) noexcept
  527. {
  528. // This method must not be called when the context has already been attached!
  529. // Call it before attaching your context, or use detach() first, before calling this!
  530. jassert (nativeContext == nullptr);
  531. useMultisampling = b;
  532. }
  533. void OpenGLContext::setOpenGLVersionRequired (OpenGLVersion v) noexcept
  534. {
  535. versionRequired = v;
  536. }
  537. void OpenGLContext::attachTo (Component& component)
  538. {
  539. component.repaint();
  540. if (getTargetComponent() != &component)
  541. {
  542. detach();
  543. attachment = new Attachment (*this, component);
  544. }
  545. }
  546. void OpenGLContext::detach()
  547. {
  548. attachment = nullptr;
  549. nativeContext = nullptr;
  550. }
  551. bool OpenGLContext::isAttached() const noexcept
  552. {
  553. return nativeContext != nullptr;
  554. }
  555. Component* OpenGLContext::getTargetComponent() const noexcept
  556. {
  557. return attachment != nullptr ? attachment->getComponent() : nullptr;
  558. }
  559. static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext;
  560. OpenGLContext* OpenGLContext::getCurrentContext()
  561. {
  562. return currentThreadActiveContext.get();
  563. }
  564. bool OpenGLContext::makeActive() const noexcept
  565. {
  566. OpenGLContext*& current = currentThreadActiveContext.get();
  567. if (nativeContext != nullptr && nativeContext->makeActive())
  568. {
  569. current = const_cast<OpenGLContext*> (this);
  570. return true;
  571. }
  572. current = nullptr;
  573. return false;
  574. }
  575. bool OpenGLContext::isActive() const noexcept
  576. {
  577. return nativeContext != nullptr && nativeContext->isActive();
  578. }
  579. void OpenGLContext::deactivateCurrentContext()
  580. {
  581. NativeContext::deactivateCurrentContext();
  582. currentThreadActiveContext.get() = nullptr;
  583. }
  584. void OpenGLContext::triggerRepaint()
  585. {
  586. if (CachedImage* const cachedImage = getCachedImage())
  587. cachedImage->triggerRepaint();
  588. }
  589. void OpenGLContext::swapBuffers()
  590. {
  591. if (nativeContext != nullptr)
  592. nativeContext->swapBuffers();
  593. }
  594. unsigned int OpenGLContext::getFrameBufferID() const noexcept
  595. {
  596. return nativeContext != nullptr ? nativeContext->getFrameBufferID() : 0;
  597. }
  598. bool OpenGLContext::setSwapInterval (int numFramesPerSwap)
  599. {
  600. return nativeContext != nullptr && nativeContext->setSwapInterval (numFramesPerSwap);
  601. }
  602. int OpenGLContext::getSwapInterval() const
  603. {
  604. return nativeContext != nullptr ? nativeContext->getSwapInterval() : 0;
  605. }
  606. void* OpenGLContext::getRawContext() const noexcept
  607. {
  608. return nativeContext != nullptr ? nativeContext->getRawContext() : nullptr;
  609. }
  610. OpenGLContext::CachedImage* OpenGLContext::getCachedImage() const noexcept
  611. {
  612. if (Component* const comp = getTargetComponent())
  613. return CachedImage::get (*comp);
  614. return nullptr;
  615. }
  616. bool OpenGLContext::areShadersAvailable() const
  617. {
  618. CachedImage* const c = getCachedImage();
  619. return c != nullptr && c->shadersAvailable;
  620. }
  621. ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const
  622. {
  623. jassert (name != nullptr);
  624. CachedImage* const c = getCachedImage();
  625. // This method must only be called from an openGL rendering callback.
  626. jassert (c != nullptr && nativeContext != nullptr);
  627. jassert (getCurrentContext() != nullptr);
  628. const int index = c->associatedObjectNames.indexOf (name);
  629. return index >= 0 ? c->associatedObjects.getUnchecked (index) : nullptr;
  630. }
  631. void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObject* newObject)
  632. {
  633. jassert (name != nullptr);
  634. if (CachedImage* const c = getCachedImage())
  635. {
  636. // This method must only be called from an openGL rendering callback.
  637. jassert (nativeContext != nullptr);
  638. jassert (getCurrentContext() != nullptr);
  639. const int index = c->associatedObjectNames.indexOf (name);
  640. if (index >= 0)
  641. {
  642. if (newObject != nullptr)
  643. {
  644. c->associatedObjects.set (index, newObject);
  645. }
  646. else
  647. {
  648. c->associatedObjectNames.remove (index);
  649. c->associatedObjects.remove (index);
  650. }
  651. }
  652. else if (newObject != nullptr)
  653. {
  654. c->associatedObjectNames.add (name);
  655. c->associatedObjects.add (newObject);
  656. }
  657. }
  658. }
  659. void OpenGLContext::setImageCacheSize (size_t newSize) noexcept { imageCacheMaxSize = newSize; }
  660. size_t OpenGLContext::getImageCacheSize() const noexcept { return imageCacheMaxSize; }
  661. void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
  662. const Rectangle<int>& anchorPosAndTextureSize,
  663. const int contextWidth, const int contextHeight,
  664. bool flippedVertically)
  665. {
  666. if (contextWidth <= 0 || contextHeight <= 0)
  667. return;
  668. JUCE_CHECK_OPENGL_ERROR
  669. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  670. glEnable (GL_BLEND);
  671. if (areShadersAvailable())
  672. {
  673. struct OverlayShaderProgram : public ReferenceCountedObject
  674. {
  675. OverlayShaderProgram (OpenGLContext& context)
  676. : program (context), builder (program), params (program)
  677. {}
  678. static const OverlayShaderProgram& select (OpenGLContext& context)
  679. {
  680. static const char programValueID[] = "juceGLComponentOverlayShader";
  681. OverlayShaderProgram* program = static_cast <OverlayShaderProgram*> (context.getAssociatedObject (programValueID));
  682. if (program == nullptr)
  683. {
  684. program = new OverlayShaderProgram (context);
  685. context.setAssociatedObject (programValueID, program);
  686. }
  687. program->program.use();
  688. return *program;
  689. }
  690. struct ProgramBuilder
  691. {
  692. ProgramBuilder (OpenGLShaderProgram& prog)
  693. {
  694. prog.addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (
  695. "attribute " JUCE_HIGHP " vec2 position;"
  696. "uniform " JUCE_HIGHP " vec2 screenSize;"
  697. "uniform " JUCE_HIGHP " float textureBounds[4];"
  698. "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
  699. "varying " JUCE_HIGHP " vec2 texturePos;"
  700. "void main()"
  701. "{"
  702. JUCE_HIGHP " vec2 scaled = position / (0.5 * screenSize.xy);"
  703. "gl_Position = vec4 (scaled.x - 1.0, 1.0 - scaled.y, 0, 1.0);"
  704. "texturePos = (position - vec2 (textureBounds[0], textureBounds[1])) / vec2 (textureBounds[2], textureBounds[3]);"
  705. "texturePos = vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y);"
  706. "}"));
  707. prog.addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (
  708. "uniform sampler2D imageTexture;"
  709. "varying " JUCE_HIGHP " vec2 texturePos;"
  710. "void main()"
  711. "{"
  712. "gl_FragColor = texture2D (imageTexture, texturePos);"
  713. "}"));
  714. prog.link();
  715. }
  716. };
  717. struct Params
  718. {
  719. Params (OpenGLShaderProgram& prog)
  720. : positionAttribute (prog, "position"),
  721. screenSize (prog, "screenSize"),
  722. imageTexture (prog, "imageTexture"),
  723. textureBounds (prog, "textureBounds"),
  724. vOffsetAndScale (prog, "vOffsetAndScale")
  725. {}
  726. void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flipVertically) const
  727. {
  728. const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
  729. textureBounds.set (m, 4);
  730. imageTexture.set (0);
  731. screenSize.set (targetWidth, targetHeight);
  732. vOffsetAndScale.set (flipVertically ? 0.0f : 1.0f,
  733. flipVertically ? 1.0f : -1.0f);
  734. }
  735. OpenGLShaderProgram::Attribute positionAttribute;
  736. OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
  737. };
  738. OpenGLShaderProgram program;
  739. ProgramBuilder builder;
  740. Params params;
  741. };
  742. const GLshort left = (GLshort) targetClipArea.getX();
  743. const GLshort top = (GLshort) targetClipArea.getY();
  744. const GLshort right = (GLshort) targetClipArea.getRight();
  745. const GLshort bottom = (GLshort) targetClipArea.getBottom();
  746. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  747. const OverlayShaderProgram& program = OverlayShaderProgram::select (*this);
  748. program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
  749. GLuint vertexBuffer = 0;
  750. extensions.glGenBuffers (1, &vertexBuffer);
  751. extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  752. extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
  753. const GLuint index = (GLuint) program.params.positionAttribute.attributeID;
  754. extensions.glVertexAttribPointer (index, 2, GL_SHORT, GL_FALSE, 4, 0);
  755. extensions.glEnableVertexAttribArray (index);
  756. JUCE_CHECK_OPENGL_ERROR
  757. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  758. extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  759. extensions.glUseProgram (0);
  760. extensions.glDisableVertexAttribArray (index);
  761. extensions.glDeleteBuffers (1, &vertexBuffer);
  762. }
  763. else
  764. {
  765. jassert (attachment == nullptr); // Running on an old graphics card!
  766. }
  767. JUCE_CHECK_OPENGL_ERROR
  768. }
  769. //==============================================================================
  770. #if JUCE_ANDROID
  771. void OpenGLContext::NativeContext::contextCreatedCallback()
  772. {
  773. isInsideGLCallback = true;
  774. if (CachedImage* const c = CachedImage::get (component))
  775. c->initialiseOnThread();
  776. else
  777. jassertfalse;
  778. isInsideGLCallback = false;
  779. }
  780. void OpenGLContext::NativeContext::renderCallback()
  781. {
  782. isInsideGLCallback = true;
  783. if (CachedImage* const c = CachedImage::get (component))
  784. {
  785. if (c->context.continuousRepaint)
  786. c->context.triggerRepaint();
  787. c->renderFrame();
  788. }
  789. isInsideGLCallback = false;
  790. }
  791. #endif