CompositingRunLoop.cpp [plain text]
#include "config.h"
#include "CompositingRunLoop.h"
#if USE(COORDINATED_GRAPHICS_THREADED)
#include <wtf/HashMap.h>
#include <wtf/MainThread.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/WorkQueue.h>
#if USE(GLIB_EVENT_LOOP)
#include <glib.h>
#endif
namespace WebKit {
class WorkQueuePool {
WTF_MAKE_NONCOPYABLE(WorkQueuePool);
friend class NeverDestroyed<WorkQueuePool>;
public:
static WorkQueuePool& singleton()
{
ASSERT(isMainThread());
static NeverDestroyed<WorkQueuePool> workQueuePool;
return workQueuePool;
}
void dispatch(void* context, Function<void ()>&& function)
{
ASSERT(isMainThread());
getOrCreateWorkQueueForContext(context).dispatch(WTFMove(function));
}
RunLoop& runLoop(void* context)
{
return getOrCreateWorkQueueForContext(context).runLoop();
}
void invalidate(void* context)
{
auto workQueue = m_workQueueMap.take(context);
ASSERT(workQueue);
if (m_workQueueMap.isEmpty()) {
m_sharedWorkQueue = nullptr;
m_threadCount = 0;
} else if (workQueue->hasOneRef())
m_threadCount--;
}
private:
WorkQueuePool()
{
#if PLATFORM(GTK)
m_threadCountLimit = 1;
#else
m_threadCountLimit = std::numeric_limits<unsigned>::max();
#endif
}
WorkQueue& getOrCreateWorkQueueForContext(void* context)
{
auto addResult = m_workQueueMap.add(context, nullptr);
if (addResult.isNewEntry) {
if (m_threadCount >= m_threadCountLimit) {
ASSERT(m_sharedWorkQueue);
addResult.iterator->value = m_sharedWorkQueue;
} else {
addResult.iterator->value = WorkQueue::create("org.webkit.ThreadedCompositorWorkQueue");
if (!m_threadCount)
m_sharedWorkQueue = addResult.iterator->value;
m_threadCount++;
}
}
return *addResult.iterator->value;
}
HashMap<void*, RefPtr<WorkQueue>> m_workQueueMap;
RefPtr<WorkQueue> m_sharedWorkQueue;
unsigned m_threadCount { 0 };
unsigned m_threadCountLimit;
};
CompositingRunLoop::CompositingRunLoop(std::function<void ()>&& updateFunction)
: m_updateTimer(WorkQueuePool::singleton().runLoop(this), this, &CompositingRunLoop::updateTimerFired)
, m_updateFunction(WTFMove(updateFunction))
{
#if USE(GLIB_EVENT_LOOP)
m_updateTimer.setPriority(G_PRIORITY_HIGH_IDLE);
#endif
}
CompositingRunLoop::~CompositingRunLoop()
{
WorkQueuePool::singleton().invalidate(this);
}
void CompositingRunLoop::performTask(Function<void ()>&& function)
{
ASSERT(isMainThread());
WorkQueuePool::singleton().dispatch(this, WTFMove(function));
}
void CompositingRunLoop::performTaskSync(Function<void ()>&& function)
{
ASSERT(isMainThread());
LockHolder locker(m_dispatchSyncConditionMutex);
WorkQueuePool::singleton().dispatch(this, [this, function = WTFMove(function)] {
function();
LockHolder locker(m_dispatchSyncConditionMutex);
m_dispatchSyncCondition.notifyOne();
});
m_dispatchSyncCondition.wait(m_dispatchSyncConditionMutex);
}
void CompositingRunLoop::startUpdateTimer(UpdateTiming timing)
{
if (m_updateTimer.isActive())
return;
const static double targetFPS = 60;
double nextUpdateTime = 0;
if (timing == WaitUntilNextFrame)
nextUpdateTime = std::max((1 / targetFPS) - (monotonicallyIncreasingTime() - m_lastUpdateTime), 0.0);
m_updateTimer.startOneShot(nextUpdateTime);
}
void CompositingRunLoop::stopUpdateTimer()
{
m_updateTimer.stop();
}
void CompositingRunLoop::updateTimerFired()
{
m_updateFunction();
m_lastUpdateTime = monotonicallyIncreasingTime();
}
}
#endif // USE(COORDINATED_GRAPHICS_THREADED)