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.

496 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. : flags (flags_),
  178. contextToShareListsWith (nullptr),
  179. needToUpdateViewport (true),
  180. needToDeleteContext (false),
  181. needToRepaint (true),
  182. cachedImage (nullptr)
  183. {
  184. setOpaque (true);
  185. triggerRepaint();
  186. componentWatcher = new OpenGLComponentWatcher (*this);
  187. }
  188. OpenGLComponent::~OpenGLComponent()
  189. {
  190. if (isUsingDedicatedThread())
  191. {
  192. /* If you're using a background thread, then your sub-class MUST call
  193. stopRenderThread() in its destructor! Otherwise, the thread could still
  194. be running while your sub-class isbeing destroyed, and so may make a call
  195. to your subclass's renderOpenGL() method when it no longer exists!
  196. */
  197. jassert (renderThread == nullptr);
  198. stopRenderThread();
  199. }
  200. else
  201. {
  202. deleteContext();
  203. }
  204. componentWatcher = nullptr;
  205. }
  206. void OpenGLComponent::setPixelFormat (const OpenGLPixelFormat& formatToUse)
  207. {
  208. if (! (preferredPixelFormat == formatToUse))
  209. {
  210. const ScopedLock sl (contextLock);
  211. preferredPixelFormat = formatToUse;
  212. recreateContextAsync();
  213. }
  214. }
  215. void OpenGLComponent::shareWith (OpenGLContext* c)
  216. {
  217. if (contextToShareListsWith != c)
  218. {
  219. const ScopedLock sl (contextLock);
  220. contextToShareListsWith = c;
  221. recreateContextAsync();
  222. }
  223. }
  224. void OpenGLComponent::recreateContextAsync()
  225. {
  226. const ScopedLock sl (contextLock);
  227. needToDeleteContext = true;
  228. repaint();
  229. }
  230. bool OpenGLComponent::makeCurrentRenderingTarget()
  231. {
  232. return context != nullptr && context->makeActive();
  233. }
  234. void OpenGLComponent::releaseAsRenderingTarget()
  235. {
  236. if (context != nullptr)
  237. context->makeInactive();
  238. }
  239. void OpenGLComponent::swapBuffers()
  240. {
  241. if (context != nullptr)
  242. context->swapBuffers();
  243. }
  244. void OpenGLComponent::updateContext()
  245. {
  246. if (needToDeleteContext)
  247. deleteContext();
  248. if (context == nullptr)
  249. {
  250. const ScopedLock sl (contextLock);
  251. if (context == nullptr)
  252. {
  253. context = createContext();
  254. if (context != nullptr)
  255. {
  256. #if JUCE_LINUX
  257. if (! isUsingDedicatedThread())
  258. #endif
  259. updateContextPosition();
  260. if (context->makeActive())
  261. {
  262. newOpenGLContextCreated();
  263. context->makeInactive();
  264. }
  265. }
  266. }
  267. }
  268. }
  269. void OpenGLComponent::deleteContext()
  270. {
  271. const ScopedLock sl (contextLock);
  272. if (context != nullptr)
  273. {
  274. if (context->makeActive())
  275. {
  276. cachedImage = nullptr;
  277. setCachedComponentImage (nullptr);
  278. releaseOpenGLContext();
  279. context->makeInactive();
  280. }
  281. context = nullptr;
  282. }
  283. needToDeleteContext = false;
  284. }
  285. bool OpenGLComponent::rebuildContext()
  286. {
  287. needToDeleteContext = true;
  288. updateContext();
  289. return context != nullptr && context->makeActive();
  290. }
  291. void OpenGLComponent::updateContextPosition()
  292. {
  293. needToUpdateViewport = true;
  294. if (getWidth() > 0 && getHeight() > 0)
  295. {
  296. Component* const topComp = getTopLevelComponent();
  297. if (topComp->getPeer() != nullptr)
  298. updateEmbeddedPosition (topComp->getLocalArea (this, getLocalBounds()));
  299. }
  300. }
  301. void OpenGLComponent::triggerRepaint()
  302. {
  303. // you mustn't set your own cached image object for an OpenGLComponent!
  304. jassert (cachedImage == nullptr
  305. || dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
  306. if (cachedImage == nullptr)
  307. setCachedComponentImage (cachedImage = new OpenGLCachedComponentImage (*this));
  308. cachedImage->triggerRepaint();
  309. }
  310. void OpenGLComponent::newOpenGLContextCreated() {}
  311. void OpenGLComponent::releaseOpenGLContext() {}
  312. void OpenGLComponent::paint (Graphics&) {}
  313. unsigned int OpenGLComponent::getFrameBufferID() const
  314. {
  315. return context != nullptr ? context->getFrameBufferID() : 0;
  316. }
  317. bool OpenGLComponent::performRender()
  318. {
  319. const ScopedLock sl (contextLock);
  320. #if JUCE_LINUX
  321. updateContext();
  322. #endif
  323. if (context != nullptr)
  324. {
  325. if (! makeCurrentRenderingTarget())
  326. return false;
  327. if (needToUpdateViewport)
  328. {
  329. needToUpdateViewport = false;
  330. glViewport (0, 0, getWidth(), getHeight());
  331. }
  332. renderOpenGL();
  333. if ((flags & allowSubComponents) != 0)
  334. {
  335. contextLock.exit(); // (MM must be locked before the context lock)
  336. MessageManagerLock mmLock (renderThread);
  337. contextLock.enter();
  338. if (! mmLock.lockWasGained())
  339. return false;
  340. // you mustn't set your own cached image object for an OpenGLComponent!
  341. jassert (dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
  342. const Rectangle<int> bounds (getLocalBounds());
  343. OpenGLFrameBuffer& frameBuffer = cachedImage->getFrameBuffer (bounds.getWidth(), bounds.getHeight());
  344. if (needToRepaint)
  345. {
  346. needToRepaint = false;
  347. RectangleList invalid (bounds);
  348. invalid.subtract (cachedImage->validArea);
  349. cachedImage->validArea = bounds;
  350. if (! invalid.isEmpty())
  351. {
  352. jassert (getCurrentContext() != nullptr);
  353. {
  354. OpenGLGraphicsContext g (*getCurrentContext(), frameBuffer);
  355. g.clipToRectangleList (invalid);
  356. g.setFill (Colours::transparentBlack);
  357. g.fillRect (bounds, true);
  358. g.setFill (Colours::black);
  359. paintSelf (g);
  360. }
  361. makeCurrentRenderingTarget();
  362. }
  363. }
  364. glEnable (GL_TEXTURE_2D);
  365. context->extensions.glActiveTexture (GL_TEXTURE0);
  366. glBindTexture (GL_TEXTURE_2D, frameBuffer.getTextureID());
  367. jassert (bounds.getPosition() == Point<int>());
  368. context->copyTexture (bounds, bounds);
  369. glBindTexture (GL_TEXTURE_2D, 0);
  370. }
  371. swapBuffers();
  372. }
  373. return true;
  374. }
  375. void OpenGLComponent::paintSelf (OpenGLGraphicsContext& glRenderer)
  376. {
  377. Graphics g (&glRenderer);
  378. #if JUCE_ENABLE_REPAINT_DEBUGGING
  379. g.saveState();
  380. #endif
  381. JUCE_TRY
  382. {
  383. paintEntireComponent (g, false);
  384. }
  385. JUCE_CATCH_EXCEPTION
  386. #if JUCE_ENABLE_REPAINT_DEBUGGING
  387. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  388. // clearly when things are being repainted.
  389. g.restoreState();
  390. static Random rng;
  391. g.fillAll (Colour ((uint8) rng.nextInt (255),
  392. (uint8) rng.nextInt (255),
  393. (uint8) rng.nextInt (255),
  394. (uint8) 0x50));
  395. #endif
  396. }