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.

575 lines
16KB

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