Use a shared scheduler on threads without a default scheduler

Render threads won't have a scheduler, so we create one that is
shared by all the maps rendering at this thread. The bad side
effect of this is that we need to wake up the render thread
to process events.

Mapbox GL should get rid of processing events on the render
thread. This solution is a workaround.
This commit is contained in:
Thiago Marcos P. Santos
2018-06-06 14:15:15 +03:00
parent 144c167274
commit a5ef72a051
5 changed files with 115 additions and 39 deletions

View File

@@ -65,8 +65,10 @@ add_library(qmapboxgl SHARED
platform/qt/src/qmapboxgl_map_observer.hpp
platform/qt/src/qmapboxgl_map_renderer.cpp
platform/qt/src/qmapboxgl_map_renderer.hpp
platform/qt/src/qmapboxgl_renderer_backend.hpp
platform/qt/src/qmapboxgl_renderer_backend.cpp
platform/qt/src/qmapboxgl_renderer_backend.hpp
platform/qt/src/qmapboxgl_scheduler.cpp
platform/qt/src/qmapboxgl_scheduler.hpp
platform/default/mbgl/util/default_styles.hpp
)

View File

@@ -1,12 +1,47 @@
#include "qmapboxgl_map_renderer.hpp"
#include "qmapboxgl_scheduler.hpp"
#include <QThreadStorage>
#include <QtGlobal>
static bool needsToForceScheduler() {
static QThreadStorage<bool> force;
if (!force.hasLocalData()) {
force.setLocalData(mbgl::Scheduler::GetCurrent() == nullptr);
}
return force.localData();
};
static auto *getScheduler() {
static QThreadStorage<std::shared_ptr<QMapboxGLScheduler>> scheduler;
if (!scheduler.hasLocalData()) {
scheduler.setLocalData(std::make_shared<QMapboxGLScheduler>());
}
return scheduler.localData().get();
};
QMapboxGLMapRenderer::QMapboxGLMapRenderer(qreal pixelRatio,
mbgl::DefaultFileSource &fs, mbgl::ThreadPool &tp, QMapboxGLSettings::GLContextMode mode)
: m_renderer(std::make_unique<mbgl::Renderer>(m_backend, pixelRatio, fs, tp, static_cast<mbgl::GLContextMode>(mode)))
, m_threadWithScheduler(Scheduler::GetCurrent() != nullptr)
, m_forceScheduler(needsToForceScheduler())
{
// If we don't have a Scheduler on this thread, which
// is usually the case for render threads, use a shared
// dummy scheduler that needs to be explicitly forced to
// process events.
if (m_forceScheduler) {
auto scheduler = getScheduler();
if (mbgl::Scheduler::GetCurrent() == nullptr) {
mbgl::Scheduler::SetCurrent(scheduler);
}
connect(scheduler, SIGNAL(needsProcessing()), this, SIGNAL(needsRendering()));
}
}
QMapboxGLMapRenderer::~QMapboxGLMapRenderer()
@@ -14,16 +49,6 @@ QMapboxGLMapRenderer::~QMapboxGLMapRenderer()
MBGL_VERIFY_THREAD(tid);
}
void QMapboxGLMapRenderer::schedule(std::weak_ptr<mbgl::Mailbox> mailbox)
{
std::lock_guard<std::mutex> lock(m_taskQueueMutex);
m_taskQueue.push(mailbox);
// Need to force the main thread to wake
// up this thread and process the events.
emit needsRendering();
}
void QMapboxGLMapRenderer::updateParameters(std::shared_ptr<mbgl::UpdateParameters> newParameters)
{
std::lock_guard<std::mutex> lock(m_updateMutex);
@@ -57,26 +82,10 @@ void QMapboxGLMapRenderer::render()
// The OpenGL implementation automatically enables the OpenGL context for us.
mbgl::BackendScope scope(m_backend, mbgl::BackendScope::ScopeType::Implicit);
// If we don't have a Scheduler on this thread, which
// is usually the case for render threads, use this
// object as scheduler.
if (!m_threadWithScheduler) {
Scheduler::SetCurrent(this);
}
m_renderer->render(*params);
if (!m_threadWithScheduler) {
std::queue<std::weak_ptr<mbgl::Mailbox>> taskQueue;
{
std::unique_lock<std::mutex> lock(m_taskQueueMutex);
std::swap(taskQueue, m_taskQueue);
}
while (!taskQueue.empty()) {
mbgl::Mailbox::maybeReceive(taskQueue.front());
taskQueue.pop();
}
if (m_forceScheduler) {
getScheduler()->processEvents();
}
}

View File

@@ -14,7 +14,6 @@
#include <memory>
#include <mutex>
#include <queue>
namespace mbgl {
class Renderer;
@@ -23,7 +22,7 @@ class UpdateParameters;
class QMapboxGLRendererBackend;
class QMapboxGLMapRenderer : public QObject, public mbgl::Scheduler
class QMapboxGLMapRenderer : public QObject
{
Q_OBJECT
@@ -32,9 +31,6 @@ public:
mbgl::ThreadPool &, QMapboxGLSettings::GLContextMode);
virtual ~QMapboxGLMapRenderer();
// mbgl::Scheduler implementation.
void schedule(std::weak_ptr<mbgl::Mailbox> scheduled) final;
void render();
void updateFramebuffer(quint32 fbo, const mbgl::Size &size);
void setObserver(std::shared_ptr<mbgl::RendererObserver>);
@@ -56,8 +52,5 @@ private:
QMapboxGLRendererBackend m_backend;
std::unique_ptr<mbgl::Renderer> m_renderer;
std::mutex m_taskQueueMutex;
std::queue<std::weak_ptr<mbgl::Mailbox>> m_taskQueue;
bool m_threadWithScheduler;
bool m_forceScheduler;
};

View File

@@ -0,0 +1,38 @@
#include "qmapboxgl_scheduler.hpp"
#include <mbgl/util/util.hpp>
#include <cassert>
QMapboxGLScheduler::QMapboxGLScheduler()
{
}
QMapboxGLScheduler::~QMapboxGLScheduler()
{
MBGL_VERIFY_THREAD(tid);
}
void QMapboxGLScheduler::schedule(std::weak_ptr<mbgl::Mailbox> mailbox)
{
std::lock_guard<std::mutex> lock(m_taskQueueMutex);
m_taskQueue.push(mailbox);
// Need to force the main thread to wake
// up this thread and process the events.
emit needsProcessing();
}
void QMapboxGLScheduler::processEvents()
{
std::queue<std::weak_ptr<mbgl::Mailbox>> taskQueue;
{
std::unique_lock<std::mutex> lock(m_taskQueueMutex);
std::swap(taskQueue, m_taskQueue);
}
while (!taskQueue.empty()) {
mbgl::Mailbox::maybeReceive(taskQueue.front());
taskQueue.pop();
}
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <mbgl/actor/mailbox.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/util/util.hpp>
#include <QObject>
#include <memory>
#include <mutex>
#include <queue>
class QMapboxGLScheduler : public QObject, public mbgl::Scheduler
{
Q_OBJECT
public:
QMapboxGLScheduler();
virtual ~QMapboxGLScheduler();
// mbgl::Scheduler implementation.
void schedule(std::weak_ptr<mbgl::Mailbox> scheduled) final;
void processEvents();
signals:
void needsProcessing();
private:
MBGL_STORE_THREAD(tid);
std::mutex m_taskQueueMutex;
std::queue<std::weak_ptr<mbgl::Mailbox>> m_taskQueue;
};