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.

561 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. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. OpenGLPixelFormat::OpenGLPixelFormat (const int bitsPerRGBComponent,
  21. const int alphaBits_,
  22. const int depthBufferBits_,
  23. const int stencilBufferBits_) noexcept
  24. : redBits (bitsPerRGBComponent),
  25. greenBits (bitsPerRGBComponent),
  26. blueBits (bitsPerRGBComponent),
  27. alphaBits (alphaBits_),
  28. depthBufferBits (depthBufferBits_),
  29. stencilBufferBits (stencilBufferBits_),
  30. accumulationBufferRedBits (0),
  31. accumulationBufferGreenBits (0),
  32. accumulationBufferBlueBits (0),
  33. accumulationBufferAlphaBits (0),
  34. multisamplingLevel (0)
  35. {
  36. }
  37. OpenGLPixelFormat::OpenGLPixelFormat (const OpenGLPixelFormat& other) noexcept
  38. : redBits (other.redBits),
  39. greenBits (other.greenBits),
  40. blueBits (other.blueBits),
  41. alphaBits (other.alphaBits),
  42. depthBufferBits (other.depthBufferBits),
  43. stencilBufferBits (other.stencilBufferBits),
  44. accumulationBufferRedBits (other.accumulationBufferRedBits),
  45. accumulationBufferGreenBits (other.accumulationBufferGreenBits),
  46. accumulationBufferBlueBits (other.accumulationBufferBlueBits),
  47. accumulationBufferAlphaBits (other.accumulationBufferAlphaBits),
  48. multisamplingLevel (other.multisamplingLevel)
  49. {
  50. }
  51. OpenGLPixelFormat& OpenGLPixelFormat::operator= (const OpenGLPixelFormat& other) noexcept
  52. {
  53. redBits = other.redBits;
  54. greenBits = other.greenBits;
  55. blueBits = other.blueBits;
  56. alphaBits = other.alphaBits;
  57. depthBufferBits = other.depthBufferBits;
  58. stencilBufferBits = other.stencilBufferBits;
  59. accumulationBufferRedBits = other.accumulationBufferRedBits;
  60. accumulationBufferGreenBits = other.accumulationBufferGreenBits;
  61. accumulationBufferBlueBits = other.accumulationBufferBlueBits;
  62. accumulationBufferAlphaBits = other.accumulationBufferAlphaBits;
  63. multisamplingLevel = other.multisamplingLevel;
  64. return *this;
  65. }
  66. bool OpenGLPixelFormat::operator== (const OpenGLPixelFormat& other) const noexcept
  67. {
  68. return redBits == other.redBits
  69. && greenBits == other.greenBits
  70. && blueBits == other.blueBits
  71. && alphaBits == other.alphaBits
  72. && depthBufferBits == other.depthBufferBits
  73. && stencilBufferBits == other.stencilBufferBits
  74. && accumulationBufferRedBits == other.accumulationBufferRedBits
  75. && accumulationBufferGreenBits == other.accumulationBufferGreenBits
  76. && accumulationBufferBlueBits == other.accumulationBufferBlueBits
  77. && accumulationBufferAlphaBits == other.accumulationBufferAlphaBits
  78. && multisamplingLevel == other.multisamplingLevel;
  79. }
  80. //==============================================================================
  81. static Array<OpenGLContext*> knownContexts;
  82. OpenGLContext::OpenGLContext() noexcept
  83. {
  84. knownContexts.add (this);
  85. }
  86. OpenGLContext::~OpenGLContext()
  87. {
  88. knownContexts.removeValue (this);
  89. }
  90. OpenGLContext* OpenGLContext::getCurrentContext()
  91. {
  92. for (int i = knownContexts.size(); --i >= 0;)
  93. {
  94. OpenGLContext* const oglc = knownContexts.getUnchecked(i);
  95. if (oglc->isActive())
  96. return oglc;
  97. }
  98. return nullptr;
  99. }
  100. //==============================================================================
  101. class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher
  102. {
  103. public:
  104. OpenGLComponentWatcher (OpenGLComponent& owner_)
  105. : ComponentMovementWatcher (&owner_),
  106. owner (owner_)
  107. {
  108. }
  109. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  110. {
  111. owner.updateContextPosition();
  112. }
  113. void componentPeerChanged()
  114. {
  115. owner.recreateContextAsync();
  116. }
  117. void componentVisibilityChanged()
  118. {
  119. if (! owner.isShowing())
  120. owner.stopBackgroundThread();
  121. }
  122. private:
  123. OpenGLComponent& owner;
  124. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponentWatcher);
  125. };
  126. //==============================================================================
  127. class OpenGLComponent::OpenGLComponentRenderThread : public Thread
  128. {
  129. public:
  130. OpenGLComponentRenderThread (OpenGLComponent& owner_)
  131. : Thread ("OpenGL Render"),
  132. owner (owner_)
  133. {
  134. }
  135. void run()
  136. {
  137. #if JUCE_LINUX
  138. {
  139. MessageManagerLock mml (this);
  140. if (! mml.lockWasGained())
  141. return;
  142. owner.updateContext();
  143. owner.updateContextPosition();
  144. }
  145. #endif
  146. while (! threadShouldExit())
  147. {
  148. const uint32 startOfRendering = Time::getMillisecondCounter();
  149. if (! owner.renderAndSwapBuffers())
  150. break;
  151. const int elapsed = (int) (Time::getMillisecondCounter() - startOfRendering);
  152. Thread::sleep (jmax (1, 20 - elapsed));
  153. }
  154. #if JUCE_LINUX
  155. owner.deleteContext();
  156. #endif
  157. }
  158. private:
  159. OpenGLComponent& owner;
  160. JUCE_DECLARE_NON_COPYABLE (OpenGLComponentRenderThread);
  161. };
  162. void OpenGLComponent::startRenderThread()
  163. {
  164. if (renderThread == nullptr)
  165. renderThread = new OpenGLComponentRenderThread (*this);
  166. renderThread->startThread (6);
  167. }
  168. void OpenGLComponent::stopRenderThread()
  169. {
  170. if (renderThread != nullptr)
  171. {
  172. renderThread->stopThread (5000);
  173. renderThread = nullptr;
  174. }
  175. #if ! JUCE_LINUX
  176. deleteContext();
  177. #endif
  178. }
  179. //==============================================================================
  180. class OpenGLComponent::OpenGLCachedComponentImage : public CachedComponentImage,
  181. public Timer // N.B. using a Timer rather than an AsyncUpdater
  182. // to avoid scheduling problems on Windows
  183. {
  184. public:
  185. OpenGLCachedComponentImage (OpenGLComponent& owner_)
  186. : owner (owner_)
  187. {
  188. }
  189. void paint (Graphics&)
  190. {
  191. ComponentPeer* const peer = owner.getPeer();
  192. if (peer != nullptr)
  193. {
  194. const Point<int> topLeft (owner.getScreenPosition() - peer->getScreenPosition());
  195. peer->addMaskedRegion (topLeft.x, topLeft.y, owner.getWidth(), owner.getHeight());
  196. }
  197. if (owner.isUsingDedicatedThread())
  198. {
  199. if (peer != nullptr && owner.isShowing())
  200. {
  201. #if ! JUCE_LINUX
  202. owner.updateContext();
  203. #endif
  204. if (! owner.threadStarted)
  205. {
  206. owner.threadStarted = true;
  207. owner.startRenderThread();
  208. }
  209. }
  210. }
  211. else
  212. {
  213. owner.updateContext();
  214. if (isTimerRunning())
  215. timerCallback();
  216. }
  217. }
  218. void invalidateAll()
  219. {
  220. validArea.clear();
  221. triggerRepaint();
  222. }
  223. void invalidate (const Rectangle<int>& area)
  224. {
  225. validArea.subtract (area);
  226. triggerRepaint();
  227. }
  228. void releaseResources()
  229. {
  230. frameBuffer.release();
  231. }
  232. void timerCallback()
  233. {
  234. stopTimer();
  235. if (! owner.makeCurrentRenderingTarget())
  236. return;
  237. const Rectangle<int> bounds (owner.getLocalBounds());
  238. const int fbW = frameBuffer.getWidth();
  239. const int fbH = frameBuffer.getHeight();
  240. if (fbW < bounds.getWidth()
  241. || fbH < bounds.getHeight()
  242. || fbW > bounds.getWidth() + 128
  243. || fbH > bounds.getHeight() + 128
  244. || ! frameBuffer.isValid())
  245. {
  246. frameBuffer.initialise (bounds.getWidth(), bounds.getHeight());
  247. validArea.clear();
  248. }
  249. owner.renderOpenGL();
  250. if ((owner.flags & allowSubComponents) != 0)
  251. {
  252. {
  253. RectangleList invalid (bounds);
  254. invalid.subtract (validArea);
  255. validArea = bounds;
  256. if (! invalid.isEmpty())
  257. {
  258. OpenGLRenderer g (frameBuffer);
  259. g.clipToRectangleList (invalid);
  260. g.setFill (Colours::transparentBlack);
  261. g.fillRect (bounds, true);
  262. g.setFill (Colours::black);
  263. paintOwner (g);
  264. }
  265. }
  266. owner.makeCurrentRenderingTarget();
  267. OpenGLHelpers::prepareFor2D (bounds.getWidth(), bounds.getHeight());
  268. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  269. glEnable (GL_BLEND);
  270. glColor4f (1.0f, 1.0f, 1.0f, owner.getAlpha());
  271. frameBuffer.drawAt (0, (float) (bounds.getHeight() - frameBuffer.getHeight()));
  272. }
  273. owner.swapBuffers();
  274. owner.releaseAsRenderingTarget();
  275. }
  276. void triggerRepaint()
  277. {
  278. if (! owner.isUsingDedicatedThread())
  279. startTimer (1000 / 70);
  280. }
  281. private:
  282. void paintOwner (OpenGLRenderer& glRenderer)
  283. {
  284. Graphics g (&glRenderer);
  285. #if JUCE_ENABLE_REPAINT_DEBUGGING
  286. g.saveState();
  287. #endif
  288. JUCE_TRY
  289. {
  290. owner.paintEntireComponent (g, false);
  291. }
  292. JUCE_CATCH_EXCEPTION
  293. #if JUCE_ENABLE_REPAINT_DEBUGGING
  294. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  295. // clearly when things are being repainted.
  296. g.restoreState();
  297. static Random rng;
  298. g.fillAll (Colour ((uint8) rng.nextInt (255),
  299. (uint8) rng.nextInt (255),
  300. (uint8) rng.nextInt (255),
  301. (uint8) 0x50));
  302. #endif
  303. }
  304. OpenGLComponent& owner;
  305. OpenGLFrameBuffer frameBuffer;
  306. RectangleList validArea;
  307. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCachedComponentImage);
  308. };
  309. //==============================================================================
  310. OpenGLComponent::OpenGLComponent (const int flags_)
  311. : flags (flags_),
  312. contextToShareListsWith (nullptr),
  313. needToUpdateViewport (true),
  314. needToDeleteContext (false),
  315. threadStarted (false)
  316. {
  317. setOpaque (true);
  318. componentWatcher = new OpenGLComponentWatcher (*this);
  319. setCachedComponentImage (new OpenGLCachedComponentImage (*this));
  320. }
  321. OpenGLComponent::~OpenGLComponent()
  322. {
  323. stopBackgroundThread();
  324. componentWatcher = nullptr;
  325. }
  326. void OpenGLComponent::setPixelFormat (const OpenGLPixelFormat& formatToUse)
  327. {
  328. if (! (preferredPixelFormat == formatToUse))
  329. {
  330. const ScopedLock sl (contextLock);
  331. preferredPixelFormat = formatToUse;
  332. recreateContextAsync();
  333. }
  334. }
  335. void OpenGLComponent::shareWith (OpenGLContext* c)
  336. {
  337. if (contextToShareListsWith != c)
  338. {
  339. const ScopedLock sl (contextLock);
  340. contextToShareListsWith = c;
  341. recreateContextAsync();
  342. }
  343. }
  344. void OpenGLComponent::recreateContextAsync()
  345. {
  346. const ScopedLock sl (contextLock);
  347. needToDeleteContext = true;
  348. repaint();
  349. }
  350. bool OpenGLComponent::makeCurrentRenderingTarget()
  351. {
  352. return context != nullptr && context->makeActive();
  353. }
  354. void OpenGLComponent::releaseAsRenderingTarget()
  355. {
  356. if (context != nullptr)
  357. context->makeInactive();
  358. }
  359. void OpenGLComponent::swapBuffers()
  360. {
  361. if (context != nullptr)
  362. context->swapBuffers();
  363. }
  364. void OpenGLComponent::updateContext()
  365. {
  366. if (needToDeleteContext)
  367. deleteContext();
  368. if (context == nullptr)
  369. {
  370. const ScopedLock sl (contextLock);
  371. if (context == nullptr)
  372. {
  373. context = createContext();
  374. if (context != nullptr)
  375. {
  376. #if JUCE_LINUX
  377. if (! useThread)
  378. #endif
  379. updateContextPosition();
  380. if (context->makeActive())
  381. {
  382. newOpenGLContextCreated();
  383. context->makeInactive();
  384. }
  385. }
  386. }
  387. }
  388. }
  389. void OpenGLComponent::deleteContext()
  390. {
  391. const ScopedLock sl (contextLock);
  392. if (context != nullptr)
  393. {
  394. if (context->makeActive())
  395. {
  396. releaseOpenGLContext();
  397. context->makeInactive();
  398. }
  399. context = nullptr;
  400. setCachedComponentImage (nullptr);
  401. }
  402. needToDeleteContext = false;
  403. }
  404. void OpenGLComponent::updateContextPosition()
  405. {
  406. needToUpdateViewport = true;
  407. if (getWidth() > 0 && getHeight() > 0)
  408. {
  409. Component* const topComp = getTopLevelComponent();
  410. if (topComp->getPeer() != nullptr)
  411. {
  412. const ScopedLock sl (contextLock);
  413. updateEmbeddedPosition (topComp->getLocalArea (this, getLocalBounds()));
  414. }
  415. }
  416. }
  417. void OpenGLComponent::stopBackgroundThread()
  418. {
  419. if (threadStarted)
  420. {
  421. stopRenderThread();
  422. threadStarted = false;
  423. }
  424. }
  425. bool OpenGLComponent::renderAndSwapBuffers()
  426. {
  427. const ScopedLock sl (contextLock);
  428. #if JUCE_LINUX
  429. updateContext();
  430. #endif
  431. if (context != nullptr)
  432. {
  433. if (! makeCurrentRenderingTarget())
  434. return false;
  435. if (needToUpdateViewport)
  436. {
  437. needToUpdateViewport = false;
  438. glViewport (0, 0, getWidth(), getHeight());
  439. }
  440. renderOpenGL();
  441. swapBuffers();
  442. }
  443. return true;
  444. }
  445. void OpenGLComponent::triggerRepaint()
  446. {
  447. OpenGLCachedComponentImage* const c
  448. = dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage());
  449. jassert (c != nullptr); // you mustn't set your own cached image object for an OpenGLComponent!
  450. if (c != nullptr)
  451. c->triggerRepaint();
  452. }
  453. void OpenGLComponent::paint (Graphics&)
  454. {
  455. }
  456. unsigned int OpenGLComponent::getFrameBufferID() const
  457. {
  458. return context != nullptr ? context->getFrameBufferID() : 0;
  459. }
  460. END_JUCE_NAMESPACE