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.

500 lines
13KB

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