Browse Source

Added method OpenGLContext::executeOnGLThread to help executing gl house-keeping tasks on the gl thread even when the message manager is locked

tags/2021-05-28
hogliux 8 years ago
parent
commit
42305de6da
4 changed files with 211 additions and 11 deletions
  1. +27
    -8
      modules/juce_events/messages/juce_MessageManager.cpp
  2. +34
    -1
      modules/juce_events/messages/juce_MessageManager.h
  3. +104
    -2
      modules/juce_opengl/opengl/juce_OpenGLContext.cpp
  4. +46
    -0
      modules/juce_opengl/opengl/juce_OpenGLContext.h

+ 27
- 8
modules/juce_events/messages/juce_MessageManager.cpp View File

@@ -287,16 +287,24 @@ public:
//==============================================================================
MessageManagerLock::MessageManagerLock (Thread* const threadToCheck)
: blockingMessage(), locked (attemptLock (threadToCheck, nullptr))
: blockingMessage(), checker (threadToCheck, nullptr),
locked (attemptLock (threadToCheck != nullptr ? &checker : nullptr))
{
}
MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal)
: blockingMessage(), locked (attemptLock (nullptr, jobToCheckForExitSignal))
: blockingMessage(), checker (nullptr, jobToCheckForExitSignal),
locked (attemptLock (jobToCheckForExitSignal != nullptr ? &checker : nullptr))
{
}
bool MessageManagerLock::attemptLock (Thread* const threadToCheck, ThreadPoolJob* const job)
MessageManagerLock::MessageManagerLock (BailOutChecker& bailOutChecker)
: blockingMessage(), checker (nullptr, nullptr),
locked (attemptLock (&bailOutChecker))
{
}
bool MessageManagerLock::attemptLock (BailOutChecker* bailOutChecker)
{
MessageManager* const mm = MessageManager::instance;
@@ -306,7 +314,7 @@ bool MessageManagerLock::attemptLock (Thread* const threadToCheck, ThreadPoolJob
if (mm->currentThreadHasLockedMessageManager())
return true;
if (threadToCheck == nullptr && job == nullptr)
if (bailOutChecker == nullptr)
{
mm->lockingLock.enter();
}
@@ -314,8 +322,7 @@ bool MessageManagerLock::attemptLock (Thread* const threadToCheck, ThreadPoolJob
{
while (! mm->lockingLock.tryEnter())
{
if ((threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit()))
if (bailOutChecker->shouldAbortAcquiringLock())
return false;
Thread::yield();
@@ -332,8 +339,7 @@ bool MessageManagerLock::attemptLock (Thread* const threadToCheck, ThreadPoolJob
while (! blockingMessage->lockedEvent.wait (20))
{
if ((threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit()))
if (bailOutChecker != nullptr && bailOutChecker->shouldAbortAcquiringLock())
{
blockingMessage->releaseEvent.signal();
blockingMessage = nullptr;
@@ -367,6 +373,19 @@ MessageManagerLock::~MessageManagerLock() noexcept
}
}
//==============================================================================
MessageManagerLock::ThreadChecker::ThreadChecker (Thread* const threadToUse,
ThreadPoolJob* const threadJobToUse)
: threadToCheck (threadToUse), job (threadJobToUse)
{
}
bool MessageManagerLock::ThreadChecker::shouldAbortAcquiringLock()
{
return (threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit());
}
//==============================================================================
JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI();
JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI()


+ 34
- 1
modules/juce_events/messages/juce_MessageManager.h View File

@@ -35,6 +35,10 @@ class ThreadPoolJob;
class ActionListener;
class ActionBroadcaster;
//==============================================================================
#if JUCE_MODULE_AVAILABLE_juce_opengl
class OpenGLContext;
#endif
//==============================================================================
/** See MessageManager::callFunctionOnMessageThread() for use of this function type. */
@@ -312,6 +316,22 @@ public:
*/
MessageManagerLock (ThreadPoolJob* jobToCheckForExitSignal);
//==============================================================================
struct BailOutChecker
{
virtual ~BailOutChecker() {}
/** Return true if acquiring the lock should be aborted. */
virtual bool shouldAbortAcquiringLock() = 0;
};
/** This is an abstraction of the other constructors. You can pass this constructor
a functor which is periodically checked if attempting the lock should be aborted.
See the MessageManagerLock (Thread*) constructor for details on how this works.
*/
MessageManagerLock (BailOutChecker&);
//==============================================================================
/** Releases the current thread's lock on the message manager.
@@ -331,9 +351,22 @@ private:
class BlockingMessage;
friend class ReferenceCountedObjectPtr<BlockingMessage>;
ReferenceCountedObjectPtr<BlockingMessage> blockingMessage;
struct ThreadChecker : BailOutChecker
{
ThreadChecker (Thread* const, ThreadPoolJob* const);
bool shouldAbortAcquiringLock() override;
Thread* const threadToCheck;
ThreadPoolJob* const job;
};
//==============================================================================
ThreadChecker checker;
bool locked;
bool attemptLock (Thread*, ThreadPoolJob*);
//==============================================================================
bool attemptLock (BailOutChecker*);
JUCE_DECLARE_NON_COPYABLE (MessageManagerLock)
};

+ 104
- 2
modules/juce_opengl/opengl/juce_OpenGLContext.cpp View File

@@ -110,6 +110,10 @@ public:
{
if (renderThread != nullptr)
{
// make sure everything has finished executing
destroying.set (1);
execute (new DoNothingWorker(), true, true);
pause();
renderThread = nullptr;
}
@@ -207,11 +211,13 @@ public:
if (context.renderComponents && isUpdating)
{
MessageLockWorker worker (*this);
// This avoids hogging the message thread when doing intensive rendering.
if (lastMMLockReleaseTime + 1 >= Time::getMillisecondCounter())
Thread::sleep (2);
mmLock = new MessageManagerLock (this); // need to acquire this before locking the context.
mmLock = new MessageManagerLock (worker); // need to acquire this before locking the context.
if (! mmLock->lockWasGained())
return false;
@@ -225,6 +231,8 @@ public:
JUCE_CHECK_OPENGL_ERROR
doWorkWhileWaitingForLock (true);
if (context.renderer != nullptr)
{
glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
@@ -501,12 +509,97 @@ public:
nativeContext->shutdownOnRenderThread();
}
//==============================================================================
struct MessageLockWorker : MessageManagerLock::BailOutChecker
{
MessageLockWorker (CachedImage& cachedImageRequestingLock)
: owner (cachedImageRequestingLock)
{
}
bool shouldAbortAcquiringLock() override { return owner.doWorkWhileWaitingForLock (false); }
CachedImage& owner;
JUCE_DECLARE_NON_COPYABLE(MessageLockWorker)
};
//==============================================================================
struct BlockingWorker : OpenGLContext::AsyncWorker
{
BlockingWorker (OpenGLContext::AsyncWorker::Ptr && workerToUse)
: originalWorker (static_cast<OpenGLContext::AsyncWorker::Ptr&&> (workerToUse))
{}
void operator() (OpenGLContext& calleeContext)
{
if (originalWorker != nullptr)
(*originalWorker) (calleeContext);
finishedSignal.signal();
}
void block() { finishedSignal.wait (); }
OpenGLContext::AsyncWorker::Ptr originalWorker;
WaitableEvent finishedSignal;
};
bool doWorkWhileWaitingForLock (bool contextIsAlreadyActive)
{
bool contextActivated = false;
for (OpenGLContext::AsyncWorker::Ptr work = workQueue.removeAndReturn (0);
work != nullptr && (! shouldExit()); work = workQueue.removeAndReturn (0))
{
if ((! contextActivated) && (! contextIsAlreadyActive))
{
if (! context.makeActive())
break;
contextActivated = true;
}
NativeContext::Locker locker (*nativeContext);
(*work) (context);
clearGLError();
}
if (contextActivated)
OpenGLContext::deactivateCurrentContext();
return shouldExit();
}
void execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock, bool calledFromDestructor = false)
{
if (calledFromDestructor || destroying.get() == 0)
{
BlockingWorker* blocker = (shouldBlock ? new BlockingWorker (static_cast<OpenGLContext::AsyncWorker::Ptr&&> (workerToUse)) : nullptr);
OpenGLContext::AsyncWorker::Ptr worker = (blocker != nullptr ? blocker : static_cast<OpenGLContext::AsyncWorker::Ptr&&> (workerToUse));
workQueue.add (worker);
if (blocker != nullptr)
blocker->block();
}
else
jassertfalse; // you called execute AFTER you detached your openglcontext
}
//==============================================================================
static CachedImage* get (Component& c) noexcept
{
return dynamic_cast<CachedImage*> (c.getCachedComponentImage());
}
//==============================================================================
// used to push no work on to the gl thread to easily block
struct DoNothingWorker : OpenGLContext::AsyncWorker
{
DoNothingWorker() {}
void operator() (OpenGLContext&) override {}
};
//==============================================================================
ScopedPointer<NativeContext> nativeContext;
@@ -526,10 +619,11 @@ public:
WaitableEvent canPaintNowFlag, finishedPaintingFlag, repaintEvent;
bool shadersAvailable, hasInitialised;
Atomic<int> needsUpdate;
Atomic<int> needsUpdate, destroying;
uint32 lastMMLockReleaseTime;
ScopedPointer<ThreadPool> renderThread;
ReferenceCountedArray<OpenGLContext::AsyncWorker, CriticalSection> workQueue;
#if JUCE_IOS
iOSBackgroundProcessCheck backgroundProcessCheck;
@@ -909,6 +1003,14 @@ void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObjec
void OpenGLContext::setImageCacheSize (size_t newSize) noexcept { imageCacheMaxSize = newSize; }
size_t OpenGLContext::getImageCacheSize() const noexcept { return imageCacheMaxSize; }
void OpenGLContext::execute (OpenGLContext::AsyncWorker::Ptr workerToUse, bool shouldBlock)
{
if (CachedImage* const c = getCachedImage())
c->execute (static_cast<OpenGLContext::AsyncWorker::Ptr&&> (workerToUse), shouldBlock);
else
jassertfalse; // You must have attached the context to a component
}
//==============================================================================
struct DepthTestDisabler
{


+ 46
- 0
modules/juce_opengl/opengl/juce_OpenGLContext.h View File

@@ -217,6 +217,26 @@ public:
*/
int getSwapInterval() const;
//==============================================================================
/** Execute a lambda, function or functor on the OpenGL thread with an active
context.
This method will attempt to execute functor on the OpenGL thread. If
blockUntilFinished is true then the method will block until the functor
has finished executing.
This function can only be called if the context is attached to a component.
Otherwise, this function will assert.
This function is useful when you need to excute house-keeping tasks such
as allocating, deallocating textures or framebuffers. As such, the functor
will execute without locking the message thread. Therefore, it is not
intended for any drawing commands or GUI code. Any GUI code should be
executed in the OpenGLRenderer::renderOpenGL callback instead.
*/
template <typename T>
void executeOnGLThread (T&& functor, bool blockUntilFinished);
//==============================================================================
/** Returns the scale factor used by the display that is being rendered.
@@ -288,7 +308,33 @@ private:
size_t imageCacheMaxSize;
bool renderComponents, useMultisampling, continuousRepaint;
//==============================================================================
struct AsyncWorker : ReferenceCountedObject
{
typedef ReferenceCountedObjectPtr<AsyncWorker> Ptr;
virtual void operator() (OpenGLContext&) = 0;
virtual ~AsyncWorker() {}
};
template <typename T>
struct AsyncWorkerFunctor : AsyncWorker
{
AsyncWorkerFunctor (T functorToUse) : functor (functorToUse) {}
void operator() (OpenGLContext& callerContext) override { functor (callerContext); }
T functor;
JUCE_DECLARE_NON_COPYABLE(AsyncWorkerFunctor)
};
//==============================================================================
CachedImage* getCachedImage() const noexcept;
void execute (AsyncWorker::Ptr, bool);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLContext)
};
//==============================================================================
#ifndef DOXYGEN
template <typename T>
void OpenGLContext::executeOnGLThread (T&& f, bool shouldBlock) { execute (new AsyncWorkerFunctor<T> (f), shouldBlock); }
#endif

Loading…
Cancel
Save