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.

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