|
|
|
@@ -114,105 +114,6 @@ OpenGLContext* OpenGLContext::getCurrentContext() |
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OpenGLComponentWatcher (OpenGLComponent& 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& 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
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class OpenGLComponent::OpenGLCachedComponentImage : public CachedComponentImage,
|
|
|
|
public Timer // N.B. using a Timer rather than an AsyncUpdater
|
|
|
|
@@ -229,10 +130,7 @@ public: |
|
|
|
ComponentPeer* const peer = owner.getPeer();
|
|
|
|
|
|
|
|
if (peer != nullptr)
|
|
|
|
{
|
|
|
|
const Point<int> topLeft (owner.getScreenPosition() - peer->getScreenPosition());
|
|
|
|
peer->addMaskedRegion (topLeft.x, topLeft.y, owner.getWidth(), owner.getHeight());
|
|
|
|
}
|
|
|
|
peer->addMaskedRegion (owner.getScreenBounds() - peer->getScreenPosition());
|
|
|
|
|
|
|
|
if (owner.isUsingDedicatedThread())
|
|
|
|
{
|
|
|
|
@@ -279,110 +177,156 @@ public: |
|
|
|
{
|
|
|
|
stopTimer();
|
|
|
|
|
|
|
|
if (! owner.makeCurrentRenderingTarget())
|
|
|
|
return;
|
|
|
|
owner.performRender();
|
|
|
|
owner.releaseAsRenderingTarget();
|
|
|
|
}
|
|
|
|
|
|
|
|
void triggerRepaint()
|
|
|
|
{
|
|
|
|
owner.needToRepaint = true;
|
|
|
|
|
|
|
|
const Rectangle<int> bounds (owner.getLocalBounds());
|
|
|
|
if (! owner.isUsingDedicatedThread())
|
|
|
|
startTimer (1000 / 70);
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenGLFrameBuffer& getFrameBuffer (int width, int height)
|
|
|
|
{
|
|
|
|
const int fbW = frameBuffer.getWidth();
|
|
|
|
const int fbH = frameBuffer.getHeight();
|
|
|
|
|
|
|
|
if (fbW < bounds.getWidth()
|
|
|
|
|| fbH < bounds.getHeight()
|
|
|
|
|| fbW > bounds.getWidth() + 128
|
|
|
|
|| fbH > bounds.getHeight() + 128
|
|
|
|
if (fbW < width
|
|
|
|
|| fbH < height
|
|
|
|
|| fbW > width + 128
|
|
|
|
|| fbH > height + 128
|
|
|
|
|| ! frameBuffer.isValid())
|
|
|
|
{
|
|
|
|
frameBuffer.initialise (bounds.getWidth(), bounds.getHeight());
|
|
|
|
frameBuffer.initialise (width, height);
|
|
|
|
validArea.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
owner.renderOpenGL();
|
|
|
|
return frameBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((owner.flags & allowSubComponents) != 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
RectangleList invalid (bounds);
|
|
|
|
invalid.subtract (validArea);
|
|
|
|
validArea = bounds;
|
|
|
|
RectangleList validArea;
|
|
|
|
|
|
|
|
if (! invalid.isEmpty())
|
|
|
|
{
|
|
|
|
OpenGLGraphicsContext g (frameBuffer);
|
|
|
|
g.clipToRectangleList (invalid);
|
|
|
|
private:
|
|
|
|
OpenGLComponent& owner;
|
|
|
|
OpenGLFrameBuffer frameBuffer;
|
|
|
|
|
|
|
|
g.setFill (Colours::transparentBlack);
|
|
|
|
g.fillRect (bounds, true);
|
|
|
|
g.setFill (Colours::black);
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCachedComponentImage);
|
|
|
|
};
|
|
|
|
|
|
|
|
paintOwner (g);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//==============================================================================
|
|
|
|
class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OpenGLComponentWatcher (OpenGLComponent& owner_)
|
|
|
|
: ComponentMovementWatcher (&owner_),
|
|
|
|
owner (owner_)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
owner.makeCurrentRenderingTarget();
|
|
|
|
OpenGLHelpers::prepareFor2D (bounds.getWidth(), bounds.getHeight());
|
|
|
|
glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glEnable (GL_BLEND);
|
|
|
|
glColor4f (1.0f, 1.0f, 1.0f, owner.getAlpha());
|
|
|
|
frameBuffer.drawAt (0, (float) (bounds.getHeight() - frameBuffer.getHeight()));
|
|
|
|
}
|
|
|
|
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
|
|
|
|
{
|
|
|
|
owner.updateContextPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
owner.swapBuffers();
|
|
|
|
owner.releaseAsRenderingTarget();
|
|
|
|
void componentPeerChanged()
|
|
|
|
{
|
|
|
|
owner.recreateContextAsync();
|
|
|
|
}
|
|
|
|
|
|
|
|
void triggerRepaint()
|
|
|
|
void componentVisibilityChanged()
|
|
|
|
{
|
|
|
|
if (! owner.isUsingDedicatedThread())
|
|
|
|
startTimer (1000 / 70);
|
|
|
|
if (! owner.isShowing())
|
|
|
|
owner.stopBackgroundThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void paintOwner (OpenGLGraphicsContext& glRenderer)
|
|
|
|
OpenGLComponent& owner;
|
|
|
|
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponentWatcher);
|
|
|
|
};
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class OpenGLComponent::OpenGLComponentRenderThread : public Thread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OpenGLComponentRenderThread (OpenGLComponent& owner_)
|
|
|
|
: Thread ("OpenGL Render"),
|
|
|
|
owner (owner_)
|
|
|
|
{
|
|
|
|
Graphics g (&glRenderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if JUCE_ENABLE_REPAINT_DEBUGGING
|
|
|
|
g.saveState();
|
|
|
|
void run()
|
|
|
|
{
|
|
|
|
#if JUCE_LINUX
|
|
|
|
{
|
|
|
|
MessageManagerLock mml (this);
|
|
|
|
|
|
|
|
if (! mml.lockWasGained())
|
|
|
|
return;
|
|
|
|
|
|
|
|
owner.updateContext();
|
|
|
|
owner.updateContextPosition();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
JUCE_TRY
|
|
|
|
while (! threadShouldExit())
|
|
|
|
{
|
|
|
|
owner.paintEntireComponent (g, false);
|
|
|
|
const uint32 startOfRendering = Time::getMillisecondCounter();
|
|
|
|
|
|
|
|
if (! owner.performRender())
|
|
|
|
break;
|
|
|
|
|
|
|
|
const int elapsed = (int) (Time::getMillisecondCounter() - startOfRendering);
|
|
|
|
Thread::sleep (jmax (1, (1000 / 60) - elapsed));
|
|
|
|
}
|
|
|
|
JUCE_CATCH_EXCEPTION
|
|
|
|
|
|
|
|
#if JUCE_ENABLE_REPAINT_DEBUGGING
|
|
|
|
// enabling this code will fill all areas that get repainted with a colour overlay, to show
|
|
|
|
// clearly when things are being repainted.
|
|
|
|
g.restoreState();
|
|
|
|
|
|
|
|
static Random rng;
|
|
|
|
g.fillAll (Colour ((uint8) rng.nextInt (255),
|
|
|
|
(uint8) rng.nextInt (255),
|
|
|
|
(uint8) rng.nextInt (255),
|
|
|
|
(uint8) 0x50));
|
|
|
|
|
|
|
|
#if JUCE_LINUX
|
|
|
|
owner.deleteContext();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
OpenGLComponent& owner;
|
|
|
|
OpenGLFrameBuffer frameBuffer;
|
|
|
|
RectangleList validArea;
|
|
|
|
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCachedComponentImage);
|
|
|
|
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 int flags_)
|
|
|
|
: flags (flags_),
|
|
|
|
contextToShareListsWith (nullptr),
|
|
|
|
needToUpdateViewport (true),
|
|
|
|
needToDeleteContext (false),
|
|
|
|
threadStarted (false)
|
|
|
|
threadStarted (false),
|
|
|
|
needToRepaint (true)
|
|
|
|
{
|
|
|
|
setOpaque (true);
|
|
|
|
setCachedComponentImage (cachedImage = new OpenGLCachedComponentImage (*this));
|
|
|
|
componentWatcher = new OpenGLComponentWatcher (*this);
|
|
|
|
setCachedComponentImage (new OpenGLCachedComponentImage (*this));
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenGLComponent::~OpenGLComponent()
|
|
|
|
@@ -492,10 +436,7 @@ void OpenGLComponent::updateContextPosition() |
|
|
|
Component* const topComp = getTopLevelComponent();
|
|
|
|
|
|
|
|
if (topComp->getPeer() != nullptr)
|
|
|
|
{
|
|
|
|
const ScopedLock sl (contextLock);
|
|
|
|
updateEmbeddedPosition (topComp->getLocalArea (this, getLocalBounds()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -508,7 +449,24 @@ void OpenGLComponent::stopBackgroundThread() |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpenGLComponent::renderAndSwapBuffers()
|
|
|
|
void OpenGLComponent::triggerRepaint()
|
|
|
|
{
|
|
|
|
// you mustn't set your own cached image object for an OpenGLComponent!
|
|
|
|
jassert (dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
|
|
|
|
|
|
|
|
cachedImage->triggerRepaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLComponent::paint (Graphics&)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int OpenGLComponent::getFrameBufferID() const
|
|
|
|
{
|
|
|
|
return context != nullptr ? context->getFrameBufferID() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OpenGLComponent::performRender()
|
|
|
|
{
|
|
|
|
const ScopedLock sl (contextLock);
|
|
|
|
|
|
|
|
@@ -528,28 +486,80 @@ bool OpenGLComponent::renderAndSwapBuffers() |
|
|
|
}
|
|
|
|
|
|
|
|
renderOpenGL();
|
|
|
|
|
|
|
|
if (needToRepaint && (flags & allowSubComponents) != 0)
|
|
|
|
{
|
|
|
|
needToRepaint = false;
|
|
|
|
|
|
|
|
contextLock.exit(); // (MM must be locked before the context lock)
|
|
|
|
MessageManagerLock mmLock (renderThread);
|
|
|
|
contextLock.enter();
|
|
|
|
|
|
|
|
if (! mmLock.lockWasGained())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// you mustn't set your own cached image object for an OpenGLComponent!
|
|
|
|
jassert (dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage()) == cachedImage);
|
|
|
|
|
|
|
|
const Rectangle<int> bounds (getLocalBounds());
|
|
|
|
OpenGLFrameBuffer& frameBuffer = cachedImage->getFrameBuffer (bounds.getWidth(), bounds.getHeight());
|
|
|
|
|
|
|
|
{
|
|
|
|
RectangleList invalid (bounds);
|
|
|
|
invalid.subtract (cachedImage->validArea);
|
|
|
|
cachedImage->validArea = bounds;
|
|
|
|
|
|
|
|
if (! invalid.isEmpty())
|
|
|
|
{
|
|
|
|
OpenGLGraphicsContext g (frameBuffer);
|
|
|
|
g.clipToRectangleList (invalid);
|
|
|
|
|
|
|
|
g.setFill (Colours::transparentBlack);
|
|
|
|
g.fillRect (bounds, true);
|
|
|
|
g.setFill (Colours::black);
|
|
|
|
|
|
|
|
paintSelf (g);
|
|
|
|
|
|
|
|
makeCurrentRenderingTarget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenGLHelpers::prepareFor2D (bounds.getWidth(), bounds.getHeight());
|
|
|
|
glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glEnable (GL_BLEND);
|
|
|
|
glColor4f (1.0f, 1.0f, 1.0f, getAlpha());
|
|
|
|
frameBuffer.drawAt (0, (float) (bounds.getHeight() - frameBuffer.getHeight()));
|
|
|
|
}
|
|
|
|
|
|
|
|
swapBuffers();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLComponent::triggerRepaint()
|
|
|
|
void OpenGLComponent::paintSelf (OpenGLGraphicsContext& glRenderer)
|
|
|
|
{
|
|
|
|
OpenGLCachedComponentImage* const c
|
|
|
|
= dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage());
|
|
|
|
|
|
|
|
jassert (c != nullptr); // you mustn't set your own cached image object for an OpenGLComponent!
|
|
|
|
Graphics g (&glRenderer);
|
|
|
|
|
|
|
|
if (c != nullptr)
|
|
|
|
c->triggerRepaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLComponent::paint (Graphics&)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#if JUCE_ENABLE_REPAINT_DEBUGGING
|
|
|
|
g.saveState();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
unsigned int OpenGLComponent::getFrameBufferID() const
|
|
|
|
{
|
|
|
|
return context != nullptr ? context->getFrameBufferID() : 0;
|
|
|
|
JUCE_TRY
|
|
|
|
{
|
|
|
|
paintEntireComponent (g, false);
|
|
|
|
}
|
|
|
|
JUCE_CATCH_EXCEPTION
|
|
|
|
|
|
|
|
#if JUCE_ENABLE_REPAINT_DEBUGGING
|
|
|
|
// enabling this code will fill all areas that get repainted with a colour overlay, to show
|
|
|
|
// clearly when things are being repainted.
|
|
|
|
g.restoreState();
|
|
|
|
|
|
|
|
static Random rng;
|
|
|
|
g.fillAll (Colour ((uint8) rng.nextInt (255),
|
|
|
|
(uint8) rng.nextInt (255),
|
|
|
|
(uint8) rng.nextInt (255),
|
|
|
|
(uint8) 0x50));
|
|
|
|
#endif
|
|
|
|
}
|