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.

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