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.

973 lines
30KB

  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. renderComponents (true), useMultisampling (false), continuousRepaint (false)
  486. {
  487. }
  488. OpenGLContext::~OpenGLContext()
  489. {
  490. detach();
  491. }
  492. void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept
  493. {
  494. // This method must not be called when the context has already been attached!
  495. // Call it before attaching your context, or use detach() first, before calling this!
  496. jassert (nativeContext == nullptr);
  497. renderer = rendererToUse;
  498. }
  499. void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept
  500. {
  501. // This method must not be called when the context has already been attached!
  502. // Call it before attaching your context, or use detach() first, before calling this!
  503. jassert (nativeContext == nullptr);
  504. renderComponents = shouldPaintComponent;
  505. }
  506. void OpenGLContext::setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept
  507. {
  508. continuousRepaint = shouldContinuouslyRepaint;
  509. triggerRepaint();
  510. }
  511. void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept
  512. {
  513. // This method must not be called when the context has already been attached!
  514. // Call it before attaching your context, or use detach() first, before calling this!
  515. jassert (nativeContext == nullptr);
  516. pixelFormat = preferredPixelFormat;
  517. }
  518. void OpenGLContext::setNativeSharedContext (void* nativeContextToShareWith) noexcept
  519. {
  520. // This method must not be called when the context has already been attached!
  521. // Call it before attaching your context, or use detach() first, before calling this!
  522. jassert (nativeContext == nullptr);
  523. contextToShareWith = nativeContextToShareWith;
  524. }
  525. void OpenGLContext::setMultisamplingEnabled (bool b) noexcept
  526. {
  527. // This method must not be called when the context has already been attached!
  528. // Call it before attaching your context, or use detach() first, before calling this!
  529. jassert (nativeContext == nullptr);
  530. useMultisampling = b;
  531. }
  532. void OpenGLContext::setOpenGLVersionRequired (OpenGLVersion v) noexcept
  533. {
  534. versionRequired = v;
  535. }
  536. void OpenGLContext::attachTo (Component& component)
  537. {
  538. component.repaint();
  539. if (getTargetComponent() != &component)
  540. {
  541. detach();
  542. attachment = new Attachment (*this, component);
  543. }
  544. }
  545. void OpenGLContext::detach()
  546. {
  547. attachment = nullptr;
  548. nativeContext = nullptr;
  549. }
  550. bool OpenGLContext::isAttached() const noexcept
  551. {
  552. return nativeContext != nullptr;
  553. }
  554. Component* OpenGLContext::getTargetComponent() const noexcept
  555. {
  556. return attachment != nullptr ? attachment->getComponent() : nullptr;
  557. }
  558. static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext;
  559. OpenGLContext* OpenGLContext::getCurrentContext()
  560. {
  561. return currentThreadActiveContext.get();
  562. }
  563. bool OpenGLContext::makeActive() const noexcept
  564. {
  565. OpenGLContext*& current = currentThreadActiveContext.get();
  566. if (nativeContext != nullptr && nativeContext->makeActive())
  567. {
  568. current = const_cast<OpenGLContext*> (this);
  569. return true;
  570. }
  571. current = nullptr;
  572. return false;
  573. }
  574. bool OpenGLContext::isActive() const noexcept
  575. {
  576. return nativeContext != nullptr && nativeContext->isActive();
  577. }
  578. void OpenGLContext::deactivateCurrentContext()
  579. {
  580. NativeContext::deactivateCurrentContext();
  581. currentThreadActiveContext.get() = nullptr;
  582. }
  583. void OpenGLContext::triggerRepaint()
  584. {
  585. if (CachedImage* const cachedImage = getCachedImage())
  586. cachedImage->triggerRepaint();
  587. }
  588. void OpenGLContext::swapBuffers()
  589. {
  590. if (nativeContext != nullptr)
  591. nativeContext->swapBuffers();
  592. }
  593. unsigned int OpenGLContext::getFrameBufferID() const noexcept
  594. {
  595. return nativeContext != nullptr ? nativeContext->getFrameBufferID() : 0;
  596. }
  597. bool OpenGLContext::setSwapInterval (int numFramesPerSwap)
  598. {
  599. return nativeContext != nullptr && nativeContext->setSwapInterval (numFramesPerSwap);
  600. }
  601. int OpenGLContext::getSwapInterval() const
  602. {
  603. return nativeContext != nullptr ? nativeContext->getSwapInterval() : 0;
  604. }
  605. void* OpenGLContext::getRawContext() const noexcept
  606. {
  607. return nativeContext != nullptr ? nativeContext->getRawContext() : nullptr;
  608. }
  609. OpenGLContext::CachedImage* OpenGLContext::getCachedImage() const noexcept
  610. {
  611. if (Component* const comp = getTargetComponent())
  612. return CachedImage::get (*comp);
  613. return nullptr;
  614. }
  615. bool OpenGLContext::areShadersAvailable() const
  616. {
  617. CachedImage* const c = getCachedImage();
  618. return c != nullptr && c->shadersAvailable;
  619. }
  620. ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const
  621. {
  622. jassert (name != nullptr);
  623. CachedImage* const c = getCachedImage();
  624. // This method must only be called from an openGL rendering callback.
  625. jassert (c != nullptr && nativeContext != nullptr);
  626. jassert (getCurrentContext() != nullptr);
  627. const int index = c->associatedObjectNames.indexOf (name);
  628. return index >= 0 ? c->associatedObjects.getUnchecked (index) : nullptr;
  629. }
  630. void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObject* newObject)
  631. {
  632. jassert (name != nullptr);
  633. if (CachedImage* const c = getCachedImage())
  634. {
  635. // This method must only be called from an openGL rendering callback.
  636. jassert (nativeContext != nullptr);
  637. jassert (getCurrentContext() != nullptr);
  638. const int index = c->associatedObjectNames.indexOf (name);
  639. if (index >= 0)
  640. {
  641. if (newObject != nullptr)
  642. {
  643. c->associatedObjects.set (index, newObject);
  644. }
  645. else
  646. {
  647. c->associatedObjectNames.remove (index);
  648. c->associatedObjects.remove (index);
  649. }
  650. }
  651. else if (newObject != nullptr)
  652. {
  653. c->associatedObjectNames.add (name);
  654. c->associatedObjects.add (newObject);
  655. }
  656. }
  657. }
  658. void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
  659. const Rectangle<int>& anchorPosAndTextureSize,
  660. const int contextWidth, const int contextHeight,
  661. bool flippedVertically)
  662. {
  663. if (contextWidth <= 0 || contextHeight <= 0)
  664. return;
  665. JUCE_CHECK_OPENGL_ERROR
  666. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  667. glEnable (GL_BLEND);
  668. if (areShadersAvailable())
  669. {
  670. struct OverlayShaderProgram : public ReferenceCountedObject
  671. {
  672. OverlayShaderProgram (OpenGLContext& context)
  673. : program (context), builder (program), params (program)
  674. {}
  675. static const OverlayShaderProgram& select (OpenGLContext& context)
  676. {
  677. static const char programValueID[] = "juceGLComponentOverlayShader";
  678. OverlayShaderProgram* program = static_cast <OverlayShaderProgram*> (context.getAssociatedObject (programValueID));
  679. if (program == nullptr)
  680. {
  681. program = new OverlayShaderProgram (context);
  682. context.setAssociatedObject (programValueID, program);
  683. }
  684. program->program.use();
  685. return *program;
  686. }
  687. struct ProgramBuilder
  688. {
  689. ProgramBuilder (OpenGLShaderProgram& prog)
  690. {
  691. prog.addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (
  692. "attribute " JUCE_HIGHP " vec2 position;"
  693. "uniform " JUCE_HIGHP " vec2 screenSize;"
  694. "uniform " JUCE_HIGHP " float textureBounds[4];"
  695. "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
  696. "varying " JUCE_HIGHP " vec2 texturePos;"
  697. "void main()"
  698. "{"
  699. JUCE_HIGHP " vec2 scaled = position / (0.5 * screenSize.xy);"
  700. "gl_Position = vec4 (scaled.x - 1.0, 1.0 - scaled.y, 0, 1.0);"
  701. "texturePos = (position - vec2 (textureBounds[0], textureBounds[1])) / vec2 (textureBounds[2], textureBounds[3]);"
  702. "texturePos = vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y);"
  703. "}"));
  704. prog.addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (
  705. "uniform sampler2D imageTexture;"
  706. "varying " JUCE_HIGHP " vec2 texturePos;"
  707. "void main()"
  708. "{"
  709. "gl_FragColor = texture2D (imageTexture, texturePos);"
  710. "}"));
  711. prog.link();
  712. }
  713. };
  714. struct Params
  715. {
  716. Params (OpenGLShaderProgram& prog)
  717. : positionAttribute (prog, "position"),
  718. screenSize (prog, "screenSize"),
  719. imageTexture (prog, "imageTexture"),
  720. textureBounds (prog, "textureBounds"),
  721. vOffsetAndScale (prog, "vOffsetAndScale")
  722. {}
  723. void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flipVertically) const
  724. {
  725. const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
  726. textureBounds.set (m, 4);
  727. imageTexture.set (0);
  728. screenSize.set (targetWidth, targetHeight);
  729. vOffsetAndScale.set (flipVertically ? 0.0f : 1.0f,
  730. flipVertically ? 1.0f : -1.0f);
  731. }
  732. OpenGLShaderProgram::Attribute positionAttribute;
  733. OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
  734. };
  735. OpenGLShaderProgram program;
  736. ProgramBuilder builder;
  737. Params params;
  738. };
  739. const GLshort left = (GLshort) targetClipArea.getX();
  740. const GLshort top = (GLshort) targetClipArea.getY();
  741. const GLshort right = (GLshort) targetClipArea.getRight();
  742. const GLshort bottom = (GLshort) targetClipArea.getBottom();
  743. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  744. const OverlayShaderProgram& program = OverlayShaderProgram::select (*this);
  745. program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
  746. GLuint vertexBuffer = 0;
  747. extensions.glGenBuffers (1, &vertexBuffer);
  748. extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  749. extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
  750. const GLuint index = (GLuint) program.params.positionAttribute.attributeID;
  751. extensions.glVertexAttribPointer (index, 2, GL_SHORT, GL_FALSE, 4, 0);
  752. extensions.glEnableVertexAttribArray (index);
  753. JUCE_CHECK_OPENGL_ERROR
  754. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  755. extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  756. extensions.glUseProgram (0);
  757. extensions.glDisableVertexAttribArray (index);
  758. extensions.glDeleteBuffers (1, &vertexBuffer);
  759. }
  760. else
  761. {
  762. jassert (attachment == nullptr); // Running on an old graphics card!
  763. }
  764. JUCE_CHECK_OPENGL_ERROR
  765. }
  766. //==============================================================================
  767. #if JUCE_ANDROID
  768. void OpenGLContext::NativeContext::contextCreatedCallback()
  769. {
  770. isInsideGLCallback = true;
  771. if (CachedImage* const c = CachedImage::get (component))
  772. c->initialiseOnThread();
  773. else
  774. jassertfalse;
  775. isInsideGLCallback = false;
  776. }
  777. void OpenGLContext::NativeContext::renderCallback()
  778. {
  779. isInsideGLCallback = true;
  780. if (CachedImage* const c = CachedImage::get (component))
  781. {
  782. if (c->context.continuousRepaint)
  783. c->context.triggerRepaint();
  784. c->renderFrame();
  785. }
  786. isInsideGLCallback = false;
  787. }
  788. #endif