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.

487 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. void OpenGLComponent::updateContextPosition()
  286. {
  287. needToUpdateViewport = true;
  288. if (getWidth() > 0 && getHeight() > 0)
  289. {
  290. Component* const topComp = getTopLevelComponent();
  291. if (topComp->getPeer() != nullptr)
  292. updateEmbeddedPosition (topComp->getLocalArea (this, getLocalBounds()));
  293. }
  294. }
  295. void OpenGLComponent::triggerRepaint()
  296. {
  297. // you mustn't set your own cached image object for an OpenGLComponent!
  298. jassert (cachedImage == nullptr
  299. || dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
  300. if (cachedImage == nullptr)
  301. setCachedComponentImage (cachedImage = new OpenGLCachedComponentImage (*this));
  302. cachedImage->triggerRepaint();
  303. }
  304. void OpenGLComponent::newOpenGLContextCreated() {}
  305. void OpenGLComponent::releaseOpenGLContext() {}
  306. void OpenGLComponent::paint (Graphics&) {}
  307. unsigned int OpenGLComponent::getFrameBufferID() const
  308. {
  309. return context != nullptr ? context->getFrameBufferID() : 0;
  310. }
  311. bool OpenGLComponent::performRender()
  312. {
  313. const ScopedLock sl (contextLock);
  314. #if JUCE_LINUX
  315. updateContext();
  316. #endif
  317. if (context != nullptr)
  318. {
  319. if (! makeCurrentRenderingTarget())
  320. return false;
  321. if (needToUpdateViewport)
  322. {
  323. needToUpdateViewport = false;
  324. glViewport (0, 0, getWidth(), getHeight());
  325. }
  326. renderOpenGL();
  327. if ((flags & allowSubComponents) != 0)
  328. {
  329. contextLock.exit(); // (MM must be locked before the context lock)
  330. MessageManagerLock mmLock (renderThread);
  331. contextLock.enter();
  332. if (! mmLock.lockWasGained())
  333. return false;
  334. // you mustn't set your own cached image object for an OpenGLComponent!
  335. jassert (dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
  336. const Rectangle<int> bounds (getLocalBounds());
  337. OpenGLFrameBuffer& frameBuffer = cachedImage->getFrameBuffer (bounds.getWidth(), bounds.getHeight());
  338. if (needToRepaint)
  339. {
  340. needToRepaint = false;
  341. RectangleList invalid (bounds);
  342. invalid.subtract (cachedImage->validArea);
  343. cachedImage->validArea = bounds;
  344. if (! invalid.isEmpty())
  345. {
  346. jassert (getCurrentContext() != nullptr);
  347. {
  348. OpenGLGraphicsContext g (*getCurrentContext(), frameBuffer);
  349. g.clipToRectangleList (invalid);
  350. g.setFill (Colours::transparentBlack);
  351. g.fillRect (bounds, true);
  352. g.setFill (Colours::black);
  353. paintSelf (g);
  354. }
  355. makeCurrentRenderingTarget();
  356. }
  357. }
  358. glEnable (GL_TEXTURE_2D);
  359. context->extensions.glActiveTexture (GL_TEXTURE0);
  360. glBindTexture (GL_TEXTURE_2D, frameBuffer.getTextureID());
  361. jassert (bounds.getPosition() == Point<int>());
  362. context->copyTexture (bounds, bounds);
  363. glBindTexture (GL_TEXTURE_2D, 0);
  364. }
  365. swapBuffers();
  366. }
  367. return true;
  368. }
  369. void OpenGLComponent::paintSelf (OpenGLGraphicsContext& glRenderer)
  370. {
  371. Graphics g (&glRenderer);
  372. #if JUCE_ENABLE_REPAINT_DEBUGGING
  373. g.saveState();
  374. #endif
  375. JUCE_TRY
  376. {
  377. paintEntireComponent (g, false);
  378. }
  379. JUCE_CATCH_EXCEPTION
  380. #if JUCE_ENABLE_REPAINT_DEBUGGING
  381. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  382. // clearly when things are being repainted.
  383. g.restoreState();
  384. static Random rng;
  385. g.fillAll (Colour ((uint8) rng.nextInt (255),
  386. (uint8) rng.nextInt (255),
  387. (uint8) rng.nextInt (255),
  388. (uint8) 0x50));
  389. #endif
  390. }