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.

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