1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Updated the OpenGLComponent so that it can render sub-components when using a background thread, and changed the demo to use this mode (which is faster).

This commit is contained in:
jules 2011-12-23 10:33:05 +00:00
parent c38916b675
commit a997490f17
12 changed files with 253 additions and 244 deletions

View file

@ -519,6 +519,7 @@ private:
{
public:
DemoOpenGLComp (Component* contentComp)
: OpenGLComponent (useBackgroundThread | allowSubComponents)
{
addAndMakeVisible (contentComp);
}

View file

@ -1062,7 +1062,7 @@ public:
Window editorWnd = (Window) editorComp->getWindowHandle();
XReparentWindow (display, editorWnd, hostWindow, 0, 0);
#else
hostWindow = attachComponentToWindowRef (editorComp, (WindowRef) ptr);
hostWindow = attachComponentToWindowRef (editorComp, ptr);
#endif
editorComp->setVisible (true);

View file

@ -241,64 +241,58 @@ private:
//==============================================================================
MessageManagerLock::MessageManagerLock (Thread* const threadToCheck)
: locked (false)
: blockingMessage(), locked (attemptLock (threadToCheck, nullptr))
{
init (threadToCheck, nullptr);
}
MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal)
: locked (false)
: blockingMessage(), locked (attemptLock (nullptr, jobToCheckForExitSignal))
{
init (nullptr, jobToCheckForExitSignal);
}
void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const job)
bool MessageManagerLock::attemptLock (Thread* const threadToCheck, ThreadPoolJob* const job)
{
if (MessageManager::instance != nullptr)
if (MessageManager::instance == nullptr)
return false;
if (MessageManager::instance->currentThreadHasLockedMessageManager())
return true;
if (threadToCheck == nullptr && job == nullptr)
{
if (MessageManager::instance->currentThreadHasLockedMessageManager())
MessageManager::instance->lockingLock.enter();
}
else
{
while (! MessageManager::instance->lockingLock.tryEnter())
{
locked = true; // either we're on the message thread, or this is a re-entrant call.
}
else
{
if (threadToCheck == nullptr && job == nullptr)
{
MessageManager::instance->lockingLock.enter();
}
else
{
while (! MessageManager::instance->lockingLock.tryEnter())
{
if ((threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit()))
return;
if ((threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit()))
return false;
Thread::sleep (1);
}
}
blockingMessage = new BlockingMessage();
blockingMessage->post();
while (! blockingMessage->lockedEvent.wait (20))
{
if ((threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit()))
{
blockingMessage->releaseEvent.signal();
blockingMessage = nullptr;
MessageManager::instance->lockingLock.exit();
return;
}
}
jassert (MessageManager::instance->threadWithLock == 0);
MessageManager::instance->threadWithLock = Thread::getCurrentThreadId();
locked = true;
Thread::sleep (1);
}
}
blockingMessage = new BlockingMessage();
blockingMessage->post();
while (! blockingMessage->lockedEvent.wait (20))
{
if ((threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit()))
{
blockingMessage->releaseEvent.signal();
blockingMessage = nullptr;
MessageManager::instance->lockingLock.exit();
return false;
}
}
jassert (MessageManager::instance->threadWithLock == 0);
MessageManager::instance->threadWithLock = Thread::getCurrentThreadId();
return true;
}
MessageManagerLock::~MessageManagerLock() noexcept

View file

@ -273,7 +273,7 @@ public:
@endcode
*/
MessageManagerLock (Thread* threadToCheckForExitSignal = 0);
MessageManagerLock (Thread* threadToCheckForExitSignal = nullptr);
//==============================================================================
/** This has the same behaviour as the other constructor, but takes a ThreadPoolJob
@ -304,7 +304,7 @@ private:
ReferenceCountedObjectPtr<BlockingMessage> blockingMessage;
bool locked;
void init (Thread* thread, ThreadPoolJob* job);
bool attemptLock (Thread*, ThreadPoolJob*);
JUCE_DECLARE_NON_COPYABLE (MessageManagerLock);
};

View file

@ -549,9 +549,9 @@ void ComponentPeer::clearMaskedRegion()
maskedRegion.clear();
}
void ComponentPeer::addMaskedRegion (int x, int y, int w, int h)
void ComponentPeer::addMaskedRegion (const Rectangle<int>& area)
{
maskedRegion.add (x, y, w, h);
maskedRegion.add (area);
}
//==============================================================================

View file

@ -334,7 +334,7 @@ public:
The masked region is cleared each time before a paint happens, so a component
will have to make sure it calls this every time it's painted.
*/
void addMaskedRegion (int x, int y, int w, int h);
void addMaskedRegion (const Rectangle<int>& area);
//==============================================================================
/** Returns the number of currently-active peers.

View file

@ -230,6 +230,8 @@ OpenGLContext* OpenGLComponent::createContext()
void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>& bounds)
{
const ScopedLock sl (contextLock);
if (context != nullptr)
static_cast <GLESContext*> (context.get())->updateWindowPosition (bounds);
}

View file

@ -194,6 +194,8 @@ OpenGLContext* OpenGLComponent::createContext()
void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>& bounds)
{
const ScopedLock sl (contextLock);
if (context != nullptr)
{
Window embeddedWindow = static_cast<WindowedGLContext*> (context.get())->embeddedWindow;

View file

@ -282,6 +282,8 @@ void* OpenGLComponent::getNativeWindowHandle() const
void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>& bounds)
{
const ScopedLock sl (contextLock);
if (context != nullptr)
{
ComponentPeer* peer = static_cast<WindowedGLContext*> (context.get())->nativeWindow;

View file

@ -114,6 +114,108 @@ OpenGLContext* OpenGLContext::getCurrentContext()
return nullptr;
}
//==============================================================================
class OpenGLComponent::OpenGLCachedComponentImage : public CachedComponentImage,
public Timer // N.B. using a Timer rather than an AsyncUpdater
// to avoid scheduling problems on Windows
{
public:
OpenGLCachedComponentImage (OpenGLComponent& owner_)
: owner (owner_)
{
}
void paint (Graphics&)
{
ComponentPeer* const peer = owner.getPeer();
if (peer != nullptr)
peer->addMaskedRegion (owner.getScreenBounds() - peer->getScreenPosition());
if (owner.isUsingDedicatedThread())
{
if (peer != nullptr && owner.isShowing())
{
#if ! JUCE_LINUX
owner.updateContext();
#endif
if (! owner.threadStarted)
{
owner.threadStarted = true;
owner.startRenderThread();
}
}
}
else
{
owner.updateContext();
if (isTimerRunning())
timerCallback();
}
}
void invalidateAll()
{
validArea.clear();
triggerRepaint();
}
void invalidate (const Rectangle<int>& area)
{
validArea.subtract (area);
triggerRepaint();
}
void releaseResources()
{
frameBuffer.release();
}
void timerCallback()
{
stopTimer();
owner.performRender();
owner.releaseAsRenderingTarget();
}
void triggerRepaint()
{
owner.needToRepaint = true;
if (! owner.isUsingDedicatedThread())
startTimer (1000 / 70);
}
OpenGLFrameBuffer& getFrameBuffer (int width, int height)
{
const int fbW = frameBuffer.getWidth();
const int fbH = frameBuffer.getHeight();
if (fbW < width
|| fbH < height
|| fbW > width + 128
|| fbH > height + 128
|| ! frameBuffer.isValid())
{
frameBuffer.initialise (width, height);
validArea.clear();
}
return frameBuffer;
}
RectangleList validArea;
private:
OpenGLComponent& owner;
OpenGLFrameBuffer frameBuffer;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCachedComponentImage);
};
//==============================================================================
class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher
{
@ -174,11 +276,11 @@ public:
{
const uint32 startOfRendering = Time::getMillisecondCounter();
if (! owner.renderAndSwapBuffers())
if (! owner.performRender())
break;
const int elapsed = (int) (Time::getMillisecondCounter() - startOfRendering);
Thread::sleep (jmax (1, 20 - elapsed));
Thread::sleep (jmax (1, (1000 / 60) - elapsed));
}
#if JUCE_LINUX
@ -213,176 +315,18 @@ void OpenGLComponent::stopRenderThread()
#endif
}
//==============================================================================
class OpenGLComponent::OpenGLCachedComponentImage : public CachedComponentImage,
public Timer // N.B. using a Timer rather than an AsyncUpdater
// to avoid scheduling problems on Windows
{
public:
OpenGLCachedComponentImage (OpenGLComponent& owner_)
: owner (owner_)
{
}
void paint (Graphics&)
{
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());
}
if (owner.isUsingDedicatedThread())
{
if (peer != nullptr && owner.isShowing())
{
#if ! JUCE_LINUX
owner.updateContext();
#endif
if (! owner.threadStarted)
{
owner.threadStarted = true;
owner.startRenderThread();
}
}
}
else
{
owner.updateContext();
if (isTimerRunning())
timerCallback();
}
}
void invalidateAll()
{
validArea.clear();
triggerRepaint();
}
void invalidate (const Rectangle<int>& area)
{
validArea.subtract (area);
triggerRepaint();
}
void releaseResources()
{
frameBuffer.release();
}
void timerCallback()
{
stopTimer();
if (! owner.makeCurrentRenderingTarget())
return;
const Rectangle<int> bounds (owner.getLocalBounds());
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
|| ! frameBuffer.isValid())
{
frameBuffer.initialise (bounds.getWidth(), bounds.getHeight());
validArea.clear();
}
owner.renderOpenGL();
if ((owner.flags & allowSubComponents) != 0)
{
{
RectangleList invalid (bounds);
invalid.subtract (validArea);
validArea = bounds;
if (! invalid.isEmpty())
{
OpenGLGraphicsContext g (frameBuffer);
g.clipToRectangleList (invalid);
g.setFill (Colours::transparentBlack);
g.fillRect (bounds, true);
g.setFill (Colours::black);
paintOwner (g);
}
}
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()));
}
owner.swapBuffers();
owner.releaseAsRenderingTarget();
}
void triggerRepaint()
{
if (! owner.isUsingDedicatedThread())
startTimer (1000 / 70);
}
private:
void paintOwner (OpenGLGraphicsContext& glRenderer)
{
Graphics g (&glRenderer);
#if JUCE_ENABLE_REPAINT_DEBUGGING
g.saveState();
#endif
JUCE_TRY
{
owner.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
}
OpenGLComponent& owner;
OpenGLFrameBuffer frameBuffer;
RectangleList validArea;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCachedComponentImage);
};
//==============================================================================
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());
Graphics g (&glRenderer);
jassert (c != nullptr); // you mustn't set your own cached image object for an OpenGLComponent!
#if JUCE_ENABLE_REPAINT_DEBUGGING
g.saveState();
#endif
if (c != nullptr)
c->triggerRepaint();
}
void OpenGLComponent::paint (Graphics&)
{
}
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
}

View file

@ -34,6 +34,8 @@
typedef Component OpenGLBaseType;
#endif
class OpenGLGraphicsContext;
//==============================================================================
/**
A component that contains an OpenGL canvas.
@ -202,12 +204,6 @@ public:
void triggerRepaint();
//==============================================================================
/** Calls the rendering callback, and swaps the buffers afterwards.
This is called automatically by paint() when the component needs to be rendered.
Returns true if the operation succeeded.
*/
virtual bool renderAndSwapBuffers();
/** This returns a critical section that can be used to lock the current context.
Because the context that is used by this component can change, e.g. when the
@ -269,10 +265,11 @@ private:
CriticalSection contextLock;
OpenGLPixelFormat preferredPixelFormat;
bool needToUpdateViewport, needToDeleteContext, threadStarted;
bool needToUpdateViewport, needToDeleteContext, threadStarted, needToRepaint;
class OpenGLCachedComponentImage;
friend class OpenGLCachedComponentImage;
OpenGLCachedComponentImage* cachedImage;
OpenGLContext* createContext();
void updateContext();
@ -280,6 +277,10 @@ private:
void stopBackgroundThread();
void recreateContextAsync();
void updateEmbeddedPosition (const Rectangle<int>&);
bool performRender();
void paintSelf (OpenGLGraphicsContext&);
int renderAndSwapBuffers(); // (This method has been deprecated)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponent);
};

View file

@ -809,10 +809,7 @@ void DirectShowComponent::paint (Graphics& g)
ComponentPeer* const peer = getPeer();
if (peer != nullptr)
{
const Point<int> topLeft (getScreenPosition() - peer->getScreenPosition());
peer->addMaskedRegion (topLeft.getX(), topLeft.getY(), getWidth(), getHeight());
}
peer->addMaskedRegion (getScreenBounds() - peer->getScreenPosition());
}
else
{