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.

999 lines
31KB

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