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.

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