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.

536 lines
15KB

  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. #if JUCE_ANDROID
  19. void triggerAndroidOpenGLRepaint (OpenGLContext*);
  20. #endif
  21. class OpenGLComponent::OpenGLCachedComponentImage : public CachedComponentImage,
  22. public Thread,
  23. public Timer // N.B. using a Timer rather than an AsyncUpdater
  24. // to avoid scheduling problems on Windows
  25. {
  26. public:
  27. OpenGLCachedComponentImage (OpenGLComponent& owner_, bool renderComponents_)
  28. : Thread ("OpenGL Rendering"),
  29. owner (owner_), needToRepaint (true),
  30. renderComponents (renderComponents_)
  31. {}
  32. void paint (Graphics&)
  33. {
  34. ComponentPeer* const peer = owner.getPeer();
  35. if (peer != nullptr)
  36. peer->addMaskedRegion (owner.getScreenBounds() - peer->getScreenPosition());
  37. if (owner.isUsingDedicatedThread())
  38. {
  39. if (peer != nullptr && owner.isShowing())
  40. {
  41. #if ! JUCE_LINUX
  42. owner.updateContext();
  43. #endif
  44. owner.startRenderThread();
  45. }
  46. }
  47. else
  48. {
  49. owner.updateContext();
  50. #if JUCE_ANDROID
  51. triggerAndroidOpenGLRepaint (owner.getCurrentContext());
  52. #else
  53. if (isTimerRunning())
  54. timerCallback();
  55. #endif
  56. }
  57. }
  58. void invalidateAll()
  59. {
  60. validArea.clear();
  61. triggerRepaint();
  62. }
  63. void invalidate (const Rectangle<int>& area)
  64. {
  65. validArea.subtract (area);
  66. triggerRepaint();
  67. }
  68. void releaseResources()
  69. {
  70. owner.makeCurrentRenderingTarget();
  71. cachedImageFrameBuffer.release();
  72. owner.releaseAsRenderingTarget();
  73. }
  74. //==============================================================================
  75. void timerCallback()
  76. {
  77. stopTimer();
  78. renderFrame();
  79. owner.releaseAsRenderingTarget();
  80. }
  81. void triggerRepaint()
  82. {
  83. needToRepaint = true;
  84. #if JUCE_ANDROID
  85. triggerAndroidOpenGLRepaint (owner.getCurrentContext());
  86. #else
  87. if (! owner.isUsingDedicatedThread())
  88. startTimer (1000 / 70);
  89. #endif
  90. }
  91. void updateContextPosition()
  92. {
  93. if (owner.getWidth() > 0 && owner.getHeight() > 0)
  94. {
  95. Component* const topComp = owner.getTopLevelComponent();
  96. if (topComp->getPeer() != nullptr)
  97. {
  98. const Rectangle<int> bounds (topComp->getLocalArea (&owner, owner.getLocalBounds()));
  99. const ScopedLock sl (owner.contextLock);
  100. if (owner.context != nullptr)
  101. owner.context->updateWindowPosition (bounds);
  102. }
  103. }
  104. }
  105. //==============================================================================
  106. void ensureFrameBufferSize (int width, int height)
  107. {
  108. const int fbW = cachedImageFrameBuffer.getWidth();
  109. const int fbH = cachedImageFrameBuffer.getHeight();
  110. if (fbW != width || fbH != height || ! cachedImageFrameBuffer.isValid())
  111. {
  112. jassert (owner.getCurrentContext() != nullptr);
  113. cachedImageFrameBuffer.initialise (*owner.getCurrentContext(), width, height);
  114. validArea.clear();
  115. JUCE_CHECK_OPENGL_ERROR
  116. }
  117. }
  118. void clearRegionInFrameBuffer (const RectangleList& list)
  119. {
  120. glClearColor (0, 0, 0, 0);
  121. glEnable (GL_SCISSOR_TEST);
  122. const GLuint previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget();
  123. cachedImageFrameBuffer.makeCurrentRenderingTarget();
  124. for (RectangleList::Iterator i (list); i.next();)
  125. {
  126. const Rectangle<int>& r = *i.getRectangle();
  127. glScissor (r.getX(), owner.getHeight() - r.getBottom(), r.getWidth(), r.getHeight());
  128. glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  129. }
  130. glDisable (GL_SCISSOR_TEST);
  131. owner.getCurrentContext()->extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget);
  132. JUCE_CHECK_OPENGL_ERROR
  133. }
  134. bool renderFrame()
  135. {
  136. const ScopedLock sl (owner.contextLock);
  137. #if JUCE_LINUX
  138. owner.updateContext();
  139. #endif
  140. OpenGLContext* const context = owner.getCurrentContext();
  141. if (context != nullptr)
  142. {
  143. if (! context->makeActive())
  144. return false;
  145. JUCE_CHECK_OPENGL_ERROR
  146. glViewport (0, 0, owner.getWidth(), owner.getHeight());
  147. owner.renderOpenGL();
  148. JUCE_CHECK_OPENGL_ERROR
  149. if (renderComponents)
  150. paintComponent (context);
  151. context->swapBuffers();
  152. }
  153. return true;
  154. }
  155. void paintComponent (OpenGLContext* const context)
  156. {
  157. jassert (context != nullptr);
  158. owner.contextLock.exit(); // (MM must be locked before the context lock)
  159. MessageManagerLock mmLock (this);
  160. owner.contextLock.enter();
  161. if (! mmLock.lockWasGained())
  162. return;
  163. // you mustn't set your own cached image object for an OpenGLComponent!
  164. jassert (dynamic_cast<OpenGLCachedComponentImage*> (owner.getCachedComponentImage()) == this);
  165. const Rectangle<int> bounds (owner.getLocalBounds());
  166. ensureFrameBufferSize (bounds.getWidth(), bounds.getHeight());
  167. if (needToRepaint)
  168. {
  169. needToRepaint = false;
  170. RectangleList invalid (bounds);
  171. invalid.subtract (validArea);
  172. validArea = bounds;
  173. if (! invalid.isEmpty())
  174. {
  175. clearRegionInFrameBuffer (invalid);
  176. {
  177. ScopedPointer<LowLevelGraphicsContext> g (createOpenGLGraphicsContext (*context, cachedImageFrameBuffer));
  178. g->clipToRectangleList (invalid);
  179. paintOwner (*g);
  180. JUCE_CHECK_OPENGL_ERROR
  181. }
  182. context->makeActive();
  183. }
  184. }
  185. JUCE_CHECK_OPENGL_ERROR
  186. #if ! JUCE_ANDROID
  187. glEnable (GL_TEXTURE_2D);
  188. #endif
  189. context->extensions.glActiveTexture (GL_TEXTURE0);
  190. glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID());
  191. jassert (bounds.getPosition() == Point<int>());
  192. context->copyTexture (bounds, bounds, context->getWidth(), context->getHeight());
  193. glBindTexture (GL_TEXTURE_2D, 0);
  194. JUCE_CHECK_OPENGL_ERROR
  195. }
  196. void paintOwner (LowLevelGraphicsContext& context)
  197. {
  198. Graphics g (&context);
  199. #if JUCE_ENABLE_REPAINT_DEBUGGING
  200. g.saveState();
  201. #endif
  202. JUCE_TRY
  203. {
  204. owner.paintEntireComponent (g, false);
  205. }
  206. JUCE_CATCH_EXCEPTION
  207. #if JUCE_ENABLE_REPAINT_DEBUGGING
  208. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  209. // clearly when things are being repainted.
  210. g.restoreState();
  211. static Random rng;
  212. g.fillAll (Colour ((uint8) rng.nextInt (255),
  213. (uint8) rng.nextInt (255),
  214. (uint8) rng.nextInt (255),
  215. (uint8) 0x50));
  216. #endif
  217. }
  218. //==============================================================================
  219. void run()
  220. {
  221. initialise();
  222. while (! threadShouldExit())
  223. {
  224. const uint32 frameRenderStartTime = Time::getMillisecondCounter();
  225. if (renderFrame())
  226. waitForNextFrame (frameRenderStartTime);
  227. }
  228. shutdown();
  229. }
  230. void initialise()
  231. {
  232. #if JUCE_LINUX
  233. MessageManagerLock mml (this);
  234. if (mml.lockWasGained())
  235. {
  236. owner.updateContext();
  237. updateContextPosition();
  238. }
  239. #endif
  240. }
  241. void shutdown()
  242. {
  243. #if JUCE_LINUX
  244. owner.deleteContext();
  245. #endif
  246. }
  247. void waitForNextFrame (const uint32 frameRenderStartTime)
  248. {
  249. const int defaultFPS = 60;
  250. const int elapsed = (int) (Time::getMillisecondCounter() - frameRenderStartTime);
  251. Thread::sleep (jmax (1, (1000 / defaultFPS) - elapsed));
  252. }
  253. //==============================================================================
  254. RectangleList validArea;
  255. OpenGLComponent& owner;
  256. OpenGLFrameBuffer cachedImageFrameBuffer;
  257. bool needToRepaint;
  258. const bool renderComponents;
  259. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCachedComponentImage);
  260. };
  261. //==============================================================================
  262. class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher
  263. {
  264. public:
  265. OpenGLComponentWatcher (OpenGLComponent& owner_)
  266. : ComponentMovementWatcher (&owner_), owner (owner_)
  267. {
  268. }
  269. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  270. {
  271. if (owner.cachedImage != nullptr)
  272. owner.cachedImage->updateContextPosition();
  273. }
  274. void componentPeerChanged()
  275. {
  276. owner.recreateContextAsync();
  277. }
  278. void componentVisibilityChanged()
  279. {
  280. if (owner.isShowing())
  281. owner.triggerRepaint();
  282. else
  283. owner.stopRenderThread();
  284. }
  285. private:
  286. OpenGLComponent& owner;
  287. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponentWatcher);
  288. };
  289. //==============================================================================
  290. OpenGLComponent::OpenGLComponent (const int flags_)
  291. #if JUCE_ANDROID
  292. : flags (flags_ & ~useBackgroundThread),
  293. #else
  294. : flags (flags_),
  295. #endif
  296. contextToShareListsWith (nullptr),
  297. needToDeleteContext (false),
  298. cachedImage (nullptr)
  299. {
  300. setOpaque (true);
  301. triggerRepaint();
  302. componentWatcher = new OpenGLComponentWatcher (*this);
  303. }
  304. OpenGLComponent::~OpenGLComponent()
  305. {
  306. if (isUsingDedicatedThread())
  307. {
  308. /* If you're using a background thread, then your sub-class MUST call
  309. stopRenderThread() in its destructor! Otherwise, the thread could still
  310. be running while your sub-class isbeing destroyed, and so may make a call
  311. to your subclass's renderOpenGL() method when it no longer exists!
  312. */
  313. jassert (! getGLCachedImage()->isThreadRunning());
  314. stopRenderThread();
  315. }
  316. else
  317. {
  318. deleteContext();
  319. }
  320. componentWatcher = nullptr;
  321. }
  322. void OpenGLComponent::setPixelFormat (const OpenGLPixelFormat& formatToUse)
  323. {
  324. if (! (preferredPixelFormat == formatToUse))
  325. {
  326. const ScopedLock sl (contextLock);
  327. preferredPixelFormat = formatToUse;
  328. recreateContextAsync();
  329. }
  330. }
  331. void OpenGLComponent::shareWith (OpenGLContext* c)
  332. {
  333. if (contextToShareListsWith != c)
  334. {
  335. const ScopedLock sl (contextLock);
  336. contextToShareListsWith = c;
  337. recreateContextAsync();
  338. }
  339. }
  340. void OpenGLComponent::startRenderThread()
  341. {
  342. getGLCachedImage()->startThread (6);
  343. }
  344. void OpenGLComponent::stopRenderThread()
  345. {
  346. getGLCachedImage()->stopThread (5000);
  347. #if ! JUCE_LINUX
  348. deleteContext();
  349. #endif
  350. }
  351. void OpenGLComponent::recreateContextAsync()
  352. {
  353. const ScopedLock sl (contextLock);
  354. needToDeleteContext = true;
  355. repaint();
  356. }
  357. bool OpenGLComponent::makeCurrentRenderingTarget()
  358. {
  359. return context != nullptr && context->makeActive();
  360. }
  361. void OpenGLComponent::releaseAsRenderingTarget()
  362. {
  363. if (context != nullptr)
  364. context->makeInactive();
  365. }
  366. void OpenGLComponent::swapBuffers()
  367. {
  368. if (context != nullptr)
  369. context->swapBuffers();
  370. }
  371. void OpenGLComponent::updateContext()
  372. {
  373. if (needToDeleteContext)
  374. deleteContext();
  375. if (context == nullptr)
  376. {
  377. const ScopedLock sl (contextLock);
  378. if (context == nullptr)
  379. {
  380. context = createContext();
  381. if (context != nullptr)
  382. {
  383. #if JUCE_LINUX
  384. if (! isUsingDedicatedThread())
  385. #endif
  386. getGLCachedImage()->updateContextPosition();
  387. if (context->makeActive())
  388. {
  389. newOpenGLContextCreated();
  390. context->makeInactive();
  391. }
  392. }
  393. }
  394. }
  395. }
  396. void OpenGLComponent::deleteContext()
  397. {
  398. const ScopedLock sl (contextLock);
  399. if (context != nullptr)
  400. {
  401. if (context->makeActive())
  402. {
  403. cachedImage = nullptr;
  404. setCachedComponentImage (nullptr);
  405. releaseOpenGLContext();
  406. context->makeInactive();
  407. }
  408. context = nullptr;
  409. }
  410. needToDeleteContext = false;
  411. }
  412. bool OpenGLComponent::rebuildContext()
  413. {
  414. needToDeleteContext = true;
  415. updateContext();
  416. return context != nullptr && context->makeActive();
  417. }
  418. OpenGLComponent::OpenGLCachedComponentImage* OpenGLComponent::getGLCachedImage()
  419. {
  420. // you mustn't set your own cached image object for an OpenGLComponent!
  421. jassert (cachedImage == nullptr
  422. || dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
  423. if (cachedImage == nullptr)
  424. setCachedComponentImage (cachedImage = new OpenGLCachedComponentImage (*this, (flags & allowSubComponents) != 0));
  425. return cachedImage;
  426. }
  427. void OpenGLComponent::triggerRepaint()
  428. {
  429. getGLCachedImage()->triggerRepaint();
  430. }
  431. void OpenGLComponent::newOpenGLContextCreated() {}
  432. void OpenGLComponent::releaseOpenGLContext() {}
  433. void OpenGLComponent::paint (Graphics&) {}