Browse Source

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).

tags/2021-05-28
jules 14 years ago
parent
commit
a997490f17
12 changed files with 251 additions and 242 deletions
  1. +1
    -0
      extras/JuceDemo/Source/MainDemoWindow.cpp
  2. +1
    -1
      modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
  3. +37
    -43
      modules/juce_events/messages/juce_MessageManager.cpp
  4. +2
    -2
      modules/juce_events/messages/juce_MessageManager.h
  5. +2
    -2
      modules/juce_gui_basics/windows/juce_ComponentPeer.cpp
  6. +1
    -1
      modules/juce_gui_basics/windows/juce_ComponentPeer.h
  7. +2
    -0
      modules/juce_opengl/native/juce_ios_OpenGLComponent.mm
  8. +2
    -0
      modules/juce_opengl/native/juce_linux_OpenGLComponent.cpp
  9. +2
    -0
      modules/juce_opengl/native/juce_win32_OpenGLComponent.cpp
  10. +192
    -182
      modules/juce_opengl/opengl/juce_OpenGLComponent.cpp
  11. +8
    -7
      modules/juce_opengl/opengl/juce_OpenGLComponent.h
  12. +1
    -4
      modules/juce_video/native/juce_win32_DirectShowComponent.cpp

+ 1
- 0
extras/JuceDemo/Source/MainDemoWindow.cpp View File

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


+ 1
- 1
modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp 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);


+ 37
- 43
modules/juce_events/messages/juce_MessageManager.cpp 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())
{
locked = true; // either we're on the message thread, or this is a re-entrant call.
}
else
MessageManager::instance->lockingLock.enter();
}
else
{
while (! MessageManager::instance->lockingLock.tryEnter())
{
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;
Thread::sleep (1);
}
}
if ((threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit()))
return false;
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;
}
}
Thread::sleep (1);
}
}
jassert (MessageManager::instance->threadWithLock == 0);
blockingMessage = new BlockingMessage();
blockingMessage->post();
MessageManager::instance->threadWithLock = Thread::getCurrentThreadId();
locked = true;
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


+ 2
- 2
modules/juce_events/messages/juce_MessageManager.h 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);
};


+ 2
- 2
modules/juce_gui_basics/windows/juce_ComponentPeer.cpp 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);
}
//==============================================================================


+ 1
- 1
modules/juce_gui_basics/windows/juce_ComponentPeer.h 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.


+ 2
- 0
modules/juce_opengl/native/juce_ios_OpenGLComponent.mm 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);
}


+ 2
- 0
modules/juce_opengl/native/juce_linux_OpenGLComponent.cpp 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;


+ 2
- 0
modules/juce_opengl/native/juce_win32_OpenGLComponent.cpp 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;


+ 192
- 182
modules/juce_opengl/opengl/juce_OpenGLComponent.cpp View File

@@ -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
}

+ 8
- 7
modules/juce_opengl/opengl/juce_OpenGLComponent.h 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);
};


+ 1
- 4
modules/juce_video/native/juce_win32_DirectShowComponent.cpp 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
{


Loading…
Cancel
Save