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.

524 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #if JUCE_ANDROID
  19. void triggerAndroidOpenGLRepaint (OpenGLContext*);
  20. #endif
  21. class OpenGLComponent::OpenGLCachedComponentImage : public CachedComponentImage,
  22. public Timer // N.B. using a Timer rather than an AsyncUpdater
  23. // to avoid scheduling problems on Windows
  24. {
  25. public:
  26. OpenGLCachedComponentImage (OpenGLComponent& owner_)
  27. : owner (owner_)
  28. {}
  29. void paint (Graphics&)
  30. {
  31. ComponentPeer* const peer = owner.getPeer();
  32. if (peer != nullptr)
  33. peer->addMaskedRegion (owner.getScreenBounds() - peer->getScreenPosition());
  34. if (owner.isUsingDedicatedThread())
  35. {
  36. if (peer != nullptr && owner.isShowing())
  37. {
  38. #if ! JUCE_LINUX
  39. owner.updateContext();
  40. #endif
  41. owner.startRenderThread();
  42. }
  43. }
  44. else
  45. {
  46. owner.updateContext();
  47. #if JUCE_ANDROID
  48. triggerAndroidOpenGLRepaint (owner.getCurrentContext());
  49. #else
  50. if (isTimerRunning())
  51. timerCallback();
  52. #endif
  53. }
  54. }
  55. void invalidateAll()
  56. {
  57. validArea.clear();
  58. triggerRepaint();
  59. }
  60. void invalidate (const Rectangle<int>& area)
  61. {
  62. validArea.subtract (area);
  63. triggerRepaint();
  64. }
  65. void releaseResources()
  66. {
  67. owner.makeCurrentRenderingTarget();
  68. frameBuffer.release();
  69. owner.releaseAsRenderingTarget();
  70. }
  71. void timerCallback()
  72. {
  73. stopTimer();
  74. owner.performRender();
  75. owner.releaseAsRenderingTarget();
  76. }
  77. void triggerRepaint()
  78. {
  79. owner.needToRepaint = true;
  80. #if JUCE_ANDROID
  81. triggerAndroidOpenGLRepaint (owner.getCurrentContext());
  82. #else
  83. if (! owner.isUsingDedicatedThread())
  84. startTimer (1000 / 70);
  85. #endif
  86. }
  87. OpenGLFrameBuffer& getFrameBuffer (int width, int height)
  88. {
  89. const int fbW = frameBuffer.getWidth();
  90. const int fbH = frameBuffer.getHeight();
  91. if (fbW != width || fbH != height || ! frameBuffer.isValid())
  92. {
  93. jassert (owner.getCurrentContext() != nullptr);
  94. frameBuffer.initialise (*owner.getCurrentContext(), width, height);
  95. validArea.clear();
  96. }
  97. return frameBuffer;
  98. }
  99. RectangleList validArea;
  100. private:
  101. OpenGLComponent& owner;
  102. OpenGLFrameBuffer frameBuffer;
  103. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCachedComponentImage);
  104. };
  105. //==============================================================================
  106. class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher
  107. {
  108. public:
  109. OpenGLComponentWatcher (OpenGLComponent& owner_)
  110. : ComponentMovementWatcher (&owner_),
  111. owner (owner_)
  112. {
  113. }
  114. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  115. {
  116. owner.updateContextPosition();
  117. }
  118. void componentPeerChanged()
  119. {
  120. owner.recreateContextAsync();
  121. }
  122. void componentVisibilityChanged()
  123. {
  124. if (! owner.isShowing())
  125. owner.stopRenderThread();
  126. }
  127. private:
  128. OpenGLComponent& owner;
  129. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponentWatcher);
  130. };
  131. //==============================================================================
  132. class OpenGLComponent::OpenGLComponentRenderThread : public Thread
  133. {
  134. public:
  135. OpenGLComponentRenderThread (OpenGLComponent& owner_)
  136. : Thread ("OpenGL Render"),
  137. owner (owner_)
  138. {
  139. }
  140. void run()
  141. {
  142. #if JUCE_LINUX
  143. {
  144. MessageManagerLock mml (this);
  145. if (! mml.lockWasGained())
  146. return;
  147. owner.updateContext();
  148. owner.updateContextPosition();
  149. }
  150. #endif
  151. while (! threadShouldExit())
  152. {
  153. const uint32 startOfRendering = Time::getMillisecondCounter();
  154. if (! owner.performRender())
  155. break;
  156. const int elapsed = (int) (Time::getMillisecondCounter() - startOfRendering);
  157. Thread::sleep (jmax (1, (1000 / 60) - elapsed));
  158. }
  159. #if JUCE_LINUX
  160. owner.deleteContext();
  161. #endif
  162. }
  163. private:
  164. OpenGLComponent& owner;
  165. JUCE_DECLARE_NON_COPYABLE (OpenGLComponentRenderThread);
  166. };
  167. void OpenGLComponent::startRenderThread()
  168. {
  169. if (renderThread == nullptr)
  170. {
  171. renderThread = new OpenGLComponentRenderThread (*this);
  172. renderThread->startThread (6);
  173. }
  174. }
  175. void OpenGLComponent::stopRenderThread()
  176. {
  177. if (renderThread != nullptr)
  178. {
  179. renderThread->stopThread (5000);
  180. renderThread = nullptr;
  181. }
  182. #if ! JUCE_LINUX
  183. deleteContext();
  184. #endif
  185. }
  186. //==============================================================================
  187. OpenGLComponent::OpenGLComponent (const int flags_)
  188. #if JUCE_ANDROID
  189. : flags (flags_ & ~useBackgroundThread),
  190. #else
  191. : flags (flags_),
  192. #endif
  193. contextToShareListsWith (nullptr),
  194. needToUpdateViewport (true),
  195. needToDeleteContext (false),
  196. needToRepaint (true),
  197. cachedImage (nullptr)
  198. {
  199. setOpaque (true);
  200. triggerRepaint();
  201. componentWatcher = new OpenGLComponentWatcher (*this);
  202. }
  203. OpenGLComponent::~OpenGLComponent()
  204. {
  205. if (isUsingDedicatedThread())
  206. {
  207. /* If you're using a background thread, then your sub-class MUST call
  208. stopRenderThread() in its destructor! Otherwise, the thread could still
  209. be running while your sub-class isbeing destroyed, and so may make a call
  210. to your subclass's renderOpenGL() method when it no longer exists!
  211. */
  212. jassert (renderThread == nullptr);
  213. stopRenderThread();
  214. }
  215. else
  216. {
  217. deleteContext();
  218. }
  219. componentWatcher = nullptr;
  220. }
  221. void OpenGLComponent::setPixelFormat (const OpenGLPixelFormat& formatToUse)
  222. {
  223. if (! (preferredPixelFormat == formatToUse))
  224. {
  225. const ScopedLock sl (contextLock);
  226. preferredPixelFormat = formatToUse;
  227. recreateContextAsync();
  228. }
  229. }
  230. void OpenGLComponent::shareWith (OpenGLContext* c)
  231. {
  232. if (contextToShareListsWith != c)
  233. {
  234. const ScopedLock sl (contextLock);
  235. contextToShareListsWith = c;
  236. recreateContextAsync();
  237. }
  238. }
  239. void OpenGLComponent::recreateContextAsync()
  240. {
  241. const ScopedLock sl (contextLock);
  242. needToDeleteContext = true;
  243. repaint();
  244. }
  245. bool OpenGLComponent::makeCurrentRenderingTarget()
  246. {
  247. return context != nullptr && context->makeActive();
  248. }
  249. void OpenGLComponent::releaseAsRenderingTarget()
  250. {
  251. if (context != nullptr)
  252. context->makeInactive();
  253. }
  254. void OpenGLComponent::swapBuffers()
  255. {
  256. if (context != nullptr)
  257. context->swapBuffers();
  258. }
  259. void OpenGLComponent::updateContext()
  260. {
  261. if (needToDeleteContext)
  262. deleteContext();
  263. if (context == nullptr)
  264. {
  265. const ScopedLock sl (contextLock);
  266. if (context == nullptr)
  267. {
  268. context = createContext();
  269. if (context != nullptr)
  270. {
  271. #if JUCE_LINUX
  272. if (! isUsingDedicatedThread())
  273. #endif
  274. updateContextPosition();
  275. if (context->makeActive())
  276. {
  277. newOpenGLContextCreated();
  278. context->makeInactive();
  279. }
  280. }
  281. }
  282. }
  283. }
  284. void OpenGLComponent::deleteContext()
  285. {
  286. const ScopedLock sl (contextLock);
  287. if (context != nullptr)
  288. {
  289. if (context->makeActive())
  290. {
  291. cachedImage = nullptr;
  292. setCachedComponentImage (nullptr);
  293. releaseOpenGLContext();
  294. context->makeInactive();
  295. }
  296. context = nullptr;
  297. }
  298. needToDeleteContext = false;
  299. }
  300. bool OpenGLComponent::rebuildContext()
  301. {
  302. needToDeleteContext = true;
  303. updateContext();
  304. return context != nullptr && context->makeActive();
  305. }
  306. void OpenGLComponent::updateContextPosition()
  307. {
  308. needToUpdateViewport = true;
  309. if (getWidth() > 0 && getHeight() > 0)
  310. {
  311. Component* const topComp = getTopLevelComponent();
  312. if (topComp->getPeer() != nullptr)
  313. updateEmbeddedPosition (topComp->getLocalArea (this, getLocalBounds()));
  314. }
  315. }
  316. void OpenGLComponent::triggerRepaint()
  317. {
  318. // you mustn't set your own cached image object for an OpenGLComponent!
  319. jassert (cachedImage == nullptr
  320. || dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
  321. if (cachedImage == nullptr)
  322. setCachedComponentImage (cachedImage = new OpenGLCachedComponentImage (*this));
  323. cachedImage->triggerRepaint();
  324. }
  325. void OpenGLComponent::newOpenGLContextCreated() {}
  326. void OpenGLComponent::releaseOpenGLContext() {}
  327. void OpenGLComponent::paint (Graphics&) {}
  328. unsigned int OpenGLComponent::getFrameBufferID() const
  329. {
  330. return context != nullptr ? context->getFrameBufferID() : 0;
  331. }
  332. bool OpenGLComponent::performRender()
  333. {
  334. const ScopedLock sl (contextLock);
  335. #if JUCE_LINUX
  336. updateContext();
  337. #endif
  338. if (context != nullptr)
  339. {
  340. if (! makeCurrentRenderingTarget())
  341. return false;
  342. JUCE_CHECK_OPENGL_ERROR
  343. if (needToUpdateViewport)
  344. {
  345. needToUpdateViewport = false;
  346. glViewport (0, 0, getWidth(), getHeight());
  347. }
  348. JUCE_CHECK_OPENGL_ERROR
  349. renderOpenGL();
  350. JUCE_CHECK_OPENGL_ERROR
  351. if ((flags & allowSubComponents) != 0)
  352. {
  353. contextLock.exit(); // (MM must be locked before the context lock)
  354. MessageManagerLock mmLock (renderThread);
  355. contextLock.enter();
  356. if (! mmLock.lockWasGained())
  357. return false;
  358. // you mustn't set your own cached image object for an OpenGLComponent!
  359. jassert (dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
  360. const Rectangle<int> bounds (getLocalBounds());
  361. OpenGLFrameBuffer& frameBuffer = cachedImage->getFrameBuffer (bounds.getWidth(), bounds.getHeight());
  362. JUCE_CHECK_OPENGL_ERROR
  363. if (needToRepaint)
  364. {
  365. needToRepaint = false;
  366. RectangleList invalid (bounds);
  367. invalid.subtract (cachedImage->validArea);
  368. cachedImage->validArea = bounds;
  369. if (! invalid.isEmpty())
  370. {
  371. jassert (getCurrentContext() != nullptr);
  372. {
  373. OpenGLGraphicsContext g (*getCurrentContext(), frameBuffer);
  374. JUCE_CHECK_OPENGL_ERROR
  375. g.clipToRectangleList (invalid);
  376. g.setFill (Colours::transparentBlack);
  377. g.fillRect (bounds, true);
  378. g.setFill (Colours::black);
  379. JUCE_CHECK_OPENGL_ERROR
  380. paintSelf (g);
  381. JUCE_CHECK_OPENGL_ERROR
  382. }
  383. makeCurrentRenderingTarget();
  384. }
  385. }
  386. JUCE_CHECK_OPENGL_ERROR
  387. #if ! JUCE_ANDROID
  388. glEnable (GL_TEXTURE_2D);
  389. #endif
  390. context->extensions.glActiveTexture (GL_TEXTURE0);
  391. glBindTexture (GL_TEXTURE_2D, frameBuffer.getTextureID());
  392. jassert (bounds.getPosition() == Point<int>());
  393. context->copyTexture (bounds, bounds);
  394. glBindTexture (GL_TEXTURE_2D, 0);
  395. JUCE_CHECK_OPENGL_ERROR
  396. }
  397. swapBuffers();
  398. }
  399. return true;
  400. }
  401. void OpenGLComponent::paintSelf (OpenGLGraphicsContext& glRenderer)
  402. {
  403. Graphics g (&glRenderer);
  404. #if JUCE_ENABLE_REPAINT_DEBUGGING
  405. g.saveState();
  406. #endif
  407. JUCE_TRY
  408. {
  409. paintEntireComponent (g, false);
  410. }
  411. JUCE_CATCH_EXCEPTION
  412. #if JUCE_ENABLE_REPAINT_DEBUGGING
  413. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  414. // clearly when things are being repainted.
  415. g.restoreState();
  416. static Random rng;
  417. g.fillAll (Colour ((uint8) rng.nextInt (255),
  418. (uint8) rng.nextInt (255),
  419. (uint8) rng.nextInt (255),
  420. (uint8) 0x50));
  421. #endif
  422. }