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.

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