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.

922 lines
29KB

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