/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-11 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online at www.gnu.org/licenses. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ BEGIN_JUCE_NAMESPACE //============================================================================== OpenGLPixelFormat::OpenGLPixelFormat (const int bitsPerRGBComponent, const int alphaBits_, const int depthBufferBits_, const int stencilBufferBits_) : redBits (bitsPerRGBComponent), greenBits (bitsPerRGBComponent), blueBits (bitsPerRGBComponent), alphaBits (alphaBits_), depthBufferBits (depthBufferBits_), stencilBufferBits (stencilBufferBits_), accumulationBufferRedBits (0), accumulationBufferGreenBits (0), accumulationBufferBlueBits (0), accumulationBufferAlphaBits (0), fullSceneAntiAliasingNumSamples (0) { } OpenGLPixelFormat::OpenGLPixelFormat (const OpenGLPixelFormat& other) : redBits (other.redBits), greenBits (other.greenBits), blueBits (other.blueBits), alphaBits (other.alphaBits), depthBufferBits (other.depthBufferBits), stencilBufferBits (other.stencilBufferBits), accumulationBufferRedBits (other.accumulationBufferRedBits), accumulationBufferGreenBits (other.accumulationBufferGreenBits), accumulationBufferBlueBits (other.accumulationBufferBlueBits), accumulationBufferAlphaBits (other.accumulationBufferAlphaBits), fullSceneAntiAliasingNumSamples (other.fullSceneAntiAliasingNumSamples) { } OpenGLPixelFormat& OpenGLPixelFormat::operator= (const OpenGLPixelFormat& other) { redBits = other.redBits; greenBits = other.greenBits; blueBits = other.blueBits; alphaBits = other.alphaBits; depthBufferBits = other.depthBufferBits; stencilBufferBits = other.stencilBufferBits; accumulationBufferRedBits = other.accumulationBufferRedBits; accumulationBufferGreenBits = other.accumulationBufferGreenBits; accumulationBufferBlueBits = other.accumulationBufferBlueBits; accumulationBufferAlphaBits = other.accumulationBufferAlphaBits; fullSceneAntiAliasingNumSamples = other.fullSceneAntiAliasingNumSamples; return *this; } bool OpenGLPixelFormat::operator== (const OpenGLPixelFormat& other) const { return redBits == other.redBits && greenBits == other.greenBits && blueBits == other.blueBits && alphaBits == other.alphaBits && depthBufferBits == other.depthBufferBits && stencilBufferBits == other.stencilBufferBits && accumulationBufferRedBits == other.accumulationBufferRedBits && accumulationBufferGreenBits == other.accumulationBufferGreenBits && accumulationBufferBlueBits == other.accumulationBufferBlueBits && accumulationBufferAlphaBits == other.accumulationBufferAlphaBits && fullSceneAntiAliasingNumSamples == other.fullSceneAntiAliasingNumSamples; } //============================================================================== static Array knownContexts; OpenGLContext::OpenGLContext() noexcept { knownContexts.add (this); } OpenGLContext::~OpenGLContext() { knownContexts.removeValue (this); } OpenGLContext* OpenGLContext::getCurrentContext() { for (int i = knownContexts.size(); --i >= 0;) { OpenGLContext* const oglc = knownContexts.getUnchecked(i); if (oglc->isActive()) return oglc; } return nullptr; } //============================================================================== class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher { public: //============================================================================== OpenGLComponentWatcher (OpenGLComponent* const owner_) : ComponentMovementWatcher (owner_), owner (owner_) { } //============================================================================== void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) { owner->updateContextPosition(); } void componentPeerChanged() { owner->recreateContextAsync(); } void componentVisibilityChanged() { if (! owner->isShowing()) owner->stopBackgroundThread(); } //============================================================================== private: OpenGLComponent* const owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponentWatcher); }; //============================================================================== class OpenGLComponent::OpenGLComponentRenderThread : public Thread { public: OpenGLComponentRenderThread (OpenGLComponent& owner_) : Thread ("OpenGL Render"), owner (owner_) { } void run() { #if JUCE_LINUX { MessageManagerLock mml (this); if (! mml.lockWasGained()) return; owner.updateContext(); owner.updateContextPosition(); } #endif while (! threadShouldExit()) { const uint32 startOfRendering = Time::getMillisecondCounter(); if (! owner.renderAndSwapBuffers()) break; const int elapsed = (int) (Time::getMillisecondCounter() - startOfRendering); Thread::sleep (jmax (1, 20 - elapsed)); } #if JUCE_LINUX owner.deleteContext(); #endif } private: OpenGLComponent& owner; JUCE_DECLARE_NON_COPYABLE (OpenGLComponentRenderThread); }; void OpenGLComponent::startRenderThread() { if (renderThread == nullptr) renderThread = new OpenGLComponentRenderThread (*this); renderThread->startThread (6); } void OpenGLComponent::stopRenderThread() { if (renderThread != nullptr) { renderThread->stopThread (5000); renderThread = nullptr; } #if ! JUCE_LINUX deleteContext(); #endif } //============================================================================== OpenGLComponent::OpenGLComponent (const OpenGLType type_, const bool useBackgroundThread) : type (type_), contextToShareListsWith (nullptr), needToUpdateViewport (true), needToDeleteContext (false), threadStarted (false), useThread (useBackgroundThread) { setOpaque (true); componentWatcher = new OpenGLComponentWatcher (this); } OpenGLComponent::~OpenGLComponent() { stopBackgroundThread(); componentWatcher = nullptr; } OpenGLPixelFormat OpenGLComponent::getPixelFormat() const { OpenGLPixelFormat pf; const ScopedLock sl (contextLock); if (context != nullptr) pf = context->getPixelFormat(); return pf; } void OpenGLComponent::setPixelFormat (const OpenGLPixelFormat& formatToUse) { if (! (preferredPixelFormat == formatToUse)) { const ScopedLock sl (contextLock); preferredPixelFormat = formatToUse; recreateContextAsync(); } } void OpenGLComponent::shareWith (OpenGLContext* c) { if (contextToShareListsWith != c) { const ScopedLock sl (contextLock); contextToShareListsWith = c; recreateContextAsync(); } } void OpenGLComponent::recreateContextAsync() { const ScopedLock sl (contextLock); needToDeleteContext = true; repaint(); } bool OpenGLComponent::makeCurrentContextActive() { return context != nullptr && context->makeActive(); } void OpenGLComponent::makeCurrentContextInactive() { if (context != nullptr) context->makeInactive(); } bool OpenGLComponent::isActiveContext() const noexcept { return context != nullptr && context->isActive(); } void OpenGLComponent::swapBuffers() { if (context != nullptr) context->swapBuffers(); } void OpenGLComponent::updateContext() { if (needToDeleteContext) deleteContext(); if (context == nullptr) { const ScopedLock sl (contextLock); if (context == nullptr) { context = createContext(); if (context != nullptr) { #if JUCE_LINUX if (! useThread) #endif updateContextPosition(); if (context->makeActive()) { newOpenGLContextCreated(); context->makeInactive(); } } } } } void OpenGLComponent::deleteContext() { const ScopedLock sl (contextLock); if (context != nullptr) { if (context->makeActive()) { releaseOpenGLContext(); context->makeInactive(); } context = nullptr; } needToDeleteContext = false; } void OpenGLComponent::updateContextPosition() { needToUpdateViewport = true; if (getWidth() > 0 && getHeight() > 0) { Component* const topComp = getTopLevelComponent(); if (topComp->getPeer() != nullptr) { const ScopedLock sl (contextLock); if (context != nullptr) context->updateWindowPosition (topComp->getLocalArea (this, getLocalBounds())); } } } void OpenGLComponent::stopBackgroundThread() { if (threadStarted) { stopRenderThread(); threadStarted = false; } } void OpenGLComponent::paint (Graphics&) { ComponentPeer* const peer = getPeer(); if (useThread) { if (peer != nullptr && isShowing()) { #if ! JUCE_LINUX updateContext(); #endif if (! threadStarted) { threadStarted = true; startRenderThread(); } } } else { updateContext(); if (! renderAndSwapBuffers()) return; } if (peer != nullptr) { const Point topLeft (getScreenPosition() - peer->getScreenPosition()); peer->addMaskedRegion (topLeft.getX(), topLeft.getY(), getWidth(), getHeight()); } } bool OpenGLComponent::renderAndSwapBuffers() { const ScopedLock sl (contextLock); #if JUCE_LINUX updateContext(); #endif if (context != nullptr) { if (! makeCurrentContextActive()) return false; if (needToUpdateViewport) { needToUpdateViewport = false; glViewport (0, 0, getWidth(), getHeight()); } renderOpenGL(); swapBuffers(); } return true; } void OpenGLComponent::internalRepaint (int x, int y, int w, int h) { Component::internalRepaint (x, y, w, h); if (context != nullptr) context->repaint(); } END_JUCE_NAMESPACE