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.

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