diff options
Diffstat (limited to 'platform/qt')
21 files changed, 823 insertions, 413 deletions
diff --git a/platform/qt/README.md b/platform/qt/README.md index 4d2d887828..018f8823b6 100644 --- a/platform/qt/README.md +++ b/platform/qt/README.md @@ -1,4 +1,4 @@ -# Mapbox Qt SDK +# Mapbox Maps SDK for Qt [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) @@ -9,7 +9,7 @@ available in the Qt SDK since Qt 5.9. Use the [Qt bugtracker](https://bugreports to the plugin and this GitHub repository for bugs related to Mapbox GL Native and the Qt bindings. You should build this repository if you want to develop/contribute using the low level Qt C++ bindings or -want to contribute to the Mapbox GL Native projectusing the Qt port for debugging. +want to contribute to the Mapbox GL Native project using the Qt port for debugging. See the Mapbox Qt landing page for more details: https://www.mapbox.com/qt/ diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp index fc346cdcde..f6d5473192 100644 --- a/platform/qt/app/mapwindow.cpp +++ b/platform/qt/app/mapwindow.cpp @@ -442,10 +442,9 @@ void MapWindow::initializeGL() void MapWindow::paintGL() { m_frameDraws++; - m_map->resize(size(), size() * pixelRatio()); + m_map->resize(size()); #if QT_VERSION >= 0x050400 - // When we're using QOpenGLWidget, we need to tell Mapbox GL about the framebuffer we're using. - m_map->setFramebufferObject(defaultFramebufferObject()); + m_map->setFramebufferObject(defaultFramebufferObject(), size() * pixelRatio()); #endif m_map->render(); } diff --git a/platform/qt/config.qdocconf b/platform/qt/config.qdocconf index d6f0edbd32..9d3b8a7ef4 100644 --- a/platform/qt/config.qdocconf +++ b/platform/qt/config.qdocconf @@ -7,7 +7,7 @@ include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) Cpp.ignoretokens = Q_MAPBOXGL_EXPORT project = QMapboxGL -description = Mapbox Qt SDK +description = Mapbox Maps SDK for Qt language = Cpp outputdir = docs diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index 8b319b0453..bc18eaba59 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -26,6 +26,11 @@ public: SharedGLContext }; + enum MapMode { + Continuous = 0, + Static + }; + enum ConstrainMode { NoConstrain = 0, ConstrainHeightOnly, @@ -40,6 +45,9 @@ public: GLContextMode contextMode() const; void setContextMode(GLContextMode); + MapMode mapMode() const; + void setMapMode(MapMode); + ConstrainMode constrainMode() const; void setConstrainMode(ConstrainMode); @@ -66,6 +74,7 @@ public: private: GLContextMode m_contextMode; + MapMode m_mapMode; ConstrainMode m_constrainMode; ViewportMode m_viewportMode; @@ -191,8 +200,7 @@ public: void scaleBy(double scale, const QPointF ¢er = QPointF()); void rotateBy(const QPointF &first, const QPointF &second); - void resize(const QSize &size, const QSize &framebufferSize); - void setFramebufferObject(quint32 fbo); + void resize(const QSize &size); double metersPerPixelAtLatitude(double latitude, double zoom) const; QMapbox::ProjectedMeters projectedMetersForCoordinate(const QMapbox::Coordinate &) const; @@ -226,15 +234,27 @@ public: void setFilter(const QString &layer, const QVariant &filter); + // When rendering on a different thread, + // should be called on the render thread. + void createRenderer(); + void destroyRenderer(); + void setFramebufferObject(quint32 fbo, const QSize &size); + public slots: void render(); void connectionEstablished(); + // Commit changes, load all the resources + // and renders the map when completed. + void startStaticRender(); + signals: void needsRendering(); void mapChanged(QMapboxGL::MapChange); void copyrightsChanged(const QString ©rightsHtml); + void staticRenderFinished(const QString &error); + private: Q_DISABLE_COPY(QMapboxGL) diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake index aaae199650..d1d597bda6 100644 --- a/platform/qt/qt.cmake +++ b/platform/qt/qt.cmake @@ -61,8 +61,12 @@ add_library(qmapboxgl SHARED platform/qt/src/qmapbox.cpp platform/qt/src/qmapboxgl.cpp platform/qt/src/qmapboxgl_p.hpp - platform/qt/src/qmapboxgl_renderer_frontend_p.hpp - platform/qt/src/qmapboxgl_renderer_frontend_p.cpp + platform/qt/src/qmapboxgl_map_observer.cpp + 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/default/mbgl/util/default_styles.hpp ) diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp index ec76ebfe53..1386a4b7aa 100644 --- a/platform/qt/src/qmapbox.cpp +++ b/platform/qt/src/qmapbox.cpp @@ -24,7 +24,7 @@ namespace QMapbox { /*! \namespace QMapbox - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Contains miscellaneous Mapbox bindings used throughout QMapboxGL. */ @@ -74,7 +74,7 @@ namespace QMapbox { /*! \class QMapbox::Feature - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents \l {https://www.mapbox.com/help/define-features/}{map features} via its \a type (PointType, LineStringType or PolygonType), \a geometry, \a @@ -94,7 +94,7 @@ namespace QMapbox { /*! \class QMapbox::ShapeAnnotationGeometry - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a shape annotation geometry. */ @@ -113,7 +113,7 @@ namespace QMapbox { /*! \class QMapbox::SymbolAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt A symbol annotation comprises of its geometry and an icon identifier. */ @@ -121,7 +121,7 @@ namespace QMapbox { /*! \class QMapbox::LineAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a line annotation object, along with its properties. @@ -131,7 +131,7 @@ namespace QMapbox { /*! \class QMapbox::FillAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a fill annotation object, along with its properties. @@ -195,7 +195,7 @@ namespace QMapbox { /*! \class QMapbox::CustomLayerRenderParameters - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapbox::CustomLayerRenderParameters provides the data passed on each render pass for a custom layer. diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 2675d87862..982f4b3b35 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -1,6 +1,8 @@ #include "qmapboxgl.hpp" #include "qmapboxgl_p.hpp" -#include "qmapboxgl_renderer_frontend_p.hpp" + +#include "qmapboxgl_map_observer.hpp" +#include "qmapboxgl_renderer_observer.hpp" #include "qt_conversion.hpp" #include "qt_geojson.hpp" @@ -43,11 +45,8 @@ #if QT_VERSION >= 0x050000 #include <QGuiApplication> -#include <QWindow> -#include <QOpenGLContext> #else #include <QCoreApplication> -#include <QGLContext> #endif #include <QDebug> @@ -65,6 +64,10 @@ using namespace QMapbox; static_assert(mbgl::underlying_type(QMapboxGLSettings::UniqueGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Unique), "error"); static_assert(mbgl::underlying_type(QMapboxGLSettings::SharedGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Shared), "error"); +// mbgl::MapMode +static_assert(mbgl::underlying_type(QMapboxGLSettings::Continuous) == mbgl::underlying_type(mbgl::MapMode::Continuous), "error"); +static_assert(mbgl::underlying_type(QMapboxGLSettings::Static) == mbgl::underlying_type(mbgl::MapMode::Static), "error"); + // mbgl::ConstrainMode static_assert(mbgl::underlying_type(QMapboxGLSettings::NoConstrain) == mbgl::underlying_type(mbgl::ConstrainMode::None), "error"); static_assert(mbgl::underlying_type(QMapboxGLSettings::ConstrainHeightOnly) == mbgl::underlying_type(mbgl::ConstrainMode::HeightOnly), "error"); @@ -133,7 +136,7 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage \class QMapboxGLSettings \brief The QMapboxGLSettings class stores the initial configuration for QMapboxGL. - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapboxGLSettings is used to configure QMapboxGL at the moment of its creation. Once created, the QMapboxGLSettings of a QMapboxGL can no longer be changed. @@ -165,6 +168,27 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage */ /*! + \enum QMapboxGLSettings::MapMode + + This enum sets the map rendering mode + + \value Continuous The map will render as data arrives from the network and + react immediately to state changes. + + This is the default mode and the preferred when the map is intended to be + interactive. + + \value Static The map will no longer react to state changes and will only + be rendered when QMapboxGL::startStaticRender is called. After all the + resources are loaded, the QMapboxGL::staticRenderFinished signal is emitted. + + This mode is useful for taking a snapshot of the finished rendering result + of the map into a QImage. + + \sa mapMode() +*/ + +/*! \enum QMapboxGLSettings::ConstrainMode This enum determines if the map wraps. @@ -200,6 +224,7 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage */ QMapboxGLSettings::QMapboxGLSettings() : m_contextMode(QMapboxGLSettings::SharedGLContext) + , m_mapMode(QMapboxGLSettings::Continuous) , m_constrainMode(QMapboxGLSettings::ConstrainHeightOnly) , m_viewportMode(QMapboxGLSettings::DefaultViewport) , m_cacheMaximumSize(mbgl::util::DEFAULT_MAX_CACHE_SIZE) @@ -230,6 +255,31 @@ void QMapboxGLSettings::setContextMode(GLContextMode mode) } /*! + Returns the map mode. Static mode will emit a signal for + rendering a map only when the map is fully loaded. + Animations like style transitions and labels fading won't + be seen. + + The Continuous mode will emit the signal for every new + change on the map and it is usually what you expect for + a interactive map. + + By default, it is set to QMapboxGLSettings::Continuous. +*/ +QMapboxGLSettings::MapMode QMapboxGLSettings::mapMode() const +{ + return m_mapMode; +} + +/*! + Sets the map \a mode. +*/ +void QMapboxGLSettings::setMapMode(MapMode mode) +{ + m_mapMode = mode; +} + +/*! Returns the constrain mode. This is used to limit the map to wrap around the globe horizontally. @@ -404,7 +454,7 @@ void QMapboxGLSettings::setResourceTransform(const std::function<std::string(con \class QMapboxGL \brief The QMapboxGL class is a Qt wrapper for the Mapbox GL Native engine. - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapboxGL is a Qt friendly version the Mapbox GL Native engine using Qt types and deep integration with Qt event loop. QMapboxGL relies as much as possible @@ -1048,32 +1098,17 @@ void QMapboxGL::rotateBy(const QPointF &first, const QPointF &second) } /*! - Resize the map to \a size and scale to fit at \a framebufferSize. For - high DPI screens, the size will be smaller than the \a framebufferSize. - - This fallowing example will double the pixel density of the map for - a given \c size: - - \code - map->resize(size / 2, size); - \endcode + Resize the map to \a size_ and scale to fit at the framebuffer. For + high DPI screens, the size will be smaller than the framebuffer. */ -void QMapboxGL::resize(const QSize& size, const QSize& framebufferSize) +void QMapboxGL::resize(const QSize& size_) { - if (d_ptr->size == size && d_ptr->fbSize == framebufferSize) return; - - d_ptr->size = size; - d_ptr->fbSize = framebufferSize; + auto size = sanitizedSize(size_); - d_ptr->mapObj->setSize(sanitizedSize(size)); -} + if (d_ptr->mapObj->getSize() == size) + return; -/*! - If Mapbox GL needs to rebind the default \a fbo, it will use the - ID supplied here. -*/ -void QMapboxGL::setFramebufferObject(quint32 fbo) { - d_ptr->fbObject = fbo; + d_ptr->mapObj->setSize(size); } /*! @@ -1468,6 +1503,51 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter) } /*! + Creates the infrastructure needed for rendering the map. It + should be called before any call to render(). + + Must be called on the render thread. +*/ +void QMapboxGL::createRenderer() +{ + d_ptr->createRenderer(); +} + +/*! + Destroys the infrastructure needed for rendering the map, + releasing resources. + + Must be called on the render thread. +*/ +void QMapboxGL::destroyRenderer() +{ + d_ptr->destroyRenderer(); +} + +/*! + Start a static rendering of the current state of the map. This + should only be called when the map is initialized in static mode. + + \sa QMapboxGLSettings::MapMode +*/ +void QMapboxGL::startStaticRender() +{ + d_ptr->mapObj->renderStill([this](std::exception_ptr err) { + QString what; + + try { + if (err) { + std::rethrow_exception(err); + } + } catch(const std::exception& e) { + what = e.what(); + } + + emit staticRenderFinished(what); + }); +} + +/*! Renders the map using OpenGL draw calls. It will make sure to bind the framebuffer object before drawing; otherwise a valid OpenGL context is expected with an appropriate OpenGL viewport state set for the size of @@ -1475,28 +1555,33 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter) This function should be called only after the signal needsRendering() is emitted at least once. + + Must be called on the render thread. */ void QMapboxGL::render() { -#if defined(__APPLE__) && QT_VERSION < 0x050000 - // FIXME Qt 4.x provides an incomplete FBO at start. - // See https://bugreports.qt.io/browse/QTBUG-36802 for details. - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return; - } -#endif - - d_ptr->dirty = false; d_ptr->render(); } /*! + If Mapbox GL needs to rebind the default \a fbo, it will use the + ID supplied here. \a size is the size of the framebuffer, which + on high DPI screens is usually bigger than the map size. + + Must be called on the render thread. +*/ +void QMapboxGL::setFramebufferObject(quint32 fbo, const QSize& size) +{ + d_ptr->setFramebufferObject(fbo, size); +} + +/*! Informs the map that the network connection has been established, causing all network requests that previously timed out to be retried immediately. */ void QMapboxGL::connectionEstablished() { - d_ptr->connectionEstablished(); + mbgl::NetworkStatus::Reachable(); } /*! @@ -1510,6 +1595,16 @@ void QMapboxGL::connectionEstablished() */ /*! + \fn void QMapboxGL::staticRenderFinished(const QString &error) + + This signal is emitted when a static map is fully drawn. Usually the next + step is to extract the map from a framebuffer into a container like a + QImage. \a error is set to a message when an error occurs. + + \sa startStaticRender() +*/ + +/*! \fn void QMapboxGL::mapChanged(QMapboxGL::MapChange change) This signal is emitted when the state of the map has changed. This signal @@ -1526,186 +1621,140 @@ void QMapboxGL::connectionEstablished() \a copyrightsHtml is a string with a HTML snippet. */ -class QMapboxGLRendererFrontend; - -QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size_, qreal pixelRatio) +QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size, qreal pixelRatio_) : QObject(q) - , size(size_) - , q_ptr(q) - , fileSourceObj(sharedDefaultFileSource( + , m_fileSourceObj(sharedDefaultFileSource( settings.cacheDatabasePath().toStdString(), settings.assetPath().toStdString(), settings.cacheDatabaseMaximumSize())) - , threadPool(mbgl::sharedThreadPool()) + , m_threadPool(mbgl::sharedThreadPool()) + , m_mode(settings.contextMode()) + , m_pixelRatio(pixelRatio_) { - // Setup resource transform if needed + // Setup the FileSource + m_fileSourceObj->setAccessToken(settings.accessToken().toStdString()); + m_fileSourceObj->setAPIBaseURL(settings.apiBaseUrl().toStdString()); + if (settings.resourceTransform()) { - m_resourceTransform = - std::make_unique< mbgl::Actor<mbgl::ResourceTransform> >( *mbgl::Scheduler::GetCurrent(), - [callback = settings.resourceTransform()] - (mbgl::Resource::Kind , const std::string&& url_) -> std::string { - return callback(std::move(url_)); - } - ); - fileSourceObj->setResourceTransform(m_resourceTransform->self()); + m_resourceTransform = std::make_unique<mbgl::Actor<mbgl::ResourceTransform>>(*mbgl::Scheduler::GetCurrent(), + [callback = settings.resourceTransform()] (mbgl::Resource::Kind, const std::string &&url_) -> std::string { + return callback(std::move(url_)); + }); + m_fileSourceObj->setResourceTransform(m_resourceTransform->self()); } - // Setup and connect the renderer frontend - frontend = std::make_unique<QMapboxGLRendererFrontend>( - std::make_unique<mbgl::Renderer>(*this, pixelRatio, *fileSourceObj, *threadPool, - static_cast<mbgl::GLContextMode>(settings.contextMode())), - *this); - connect(frontend.get(), SIGNAL(updated()), this, SLOT(invalidate())); + // Setup MapObserver + m_mapObserver = std::make_unique<QMapboxGLMapObserver>(this); + + qRegisterMetaType<QMapboxGL::MapChange>("QMapboxGL::MapChange"); + + connect(m_mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q, SIGNAL(mapChanged(QMapboxGL::MapChange))); + connect(m_mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q, SIGNAL(copyrightsChanged(QString))); + // Setup the Map object mapObj = std::make_unique<mbgl::Map>( - *frontend, - *this, sanitizedSize(size), - pixelRatio, *fileSourceObj, *threadPool, - mbgl::MapMode::Continuous, + *this, // RendererFrontend + *m_mapObserver, + sanitizedSize(size), + m_pixelRatio, *m_fileSourceObj, *m_threadPool, + static_cast<mbgl::MapMode>(settings.mapMode()), static_cast<mbgl::ConstrainMode>(settings.constrainMode()), static_cast<mbgl::ViewportMode>(settings.viewportMode())); - qRegisterMetaType<QMapboxGL::MapChange>("QMapboxGL::MapChange"); - - fileSourceObj->setAccessToken(settings.accessToken().toStdString()); - fileSourceObj->setAPIBaseURL(settings.apiBaseUrl().toStdString()); - - connect(this, SIGNAL(needsRendering()), q_ptr, SIGNAL(needsRendering()), Qt::QueuedConnection); - connect(this, SIGNAL(mapChanged(QMapboxGL::MapChange)), q_ptr, SIGNAL(mapChanged(QMapboxGL::MapChange)), Qt::QueuedConnection); - connect(this, SIGNAL(copyrightsChanged(QString)), q_ptr, SIGNAL(copyrightsChanged(QString)), Qt::QueuedConnection); + // Needs to be Queued to give time to discard redundant draw calls via the `renderQueued` flag. + connect(this, SIGNAL(needsRendering()), q, SIGNAL(needsRendering()), Qt::QueuedConnection); } QMapboxGLPrivate::~QMapboxGLPrivate() { } -mbgl::Size QMapboxGLPrivate::getFramebufferSize() const { - return sanitizedSize(fbSize); -} +void QMapboxGLPrivate::update(std::shared_ptr<mbgl::UpdateParameters> parameters) +{ + std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex); -void QMapboxGLPrivate::updateAssumedState() { - assumeFramebufferBinding(fbObject); -#if QT_VERSION >= 0x050600 - assumeViewport(0, 0, getFramebufferSize()); -#endif -} + if (!m_mapRenderer) { + return; + } -void QMapboxGLPrivate::bind() { - setFramebufferBinding(fbObject); - setViewport(0, 0, getFramebufferSize()); -} + m_mapRenderer->updateParameters(std::move(parameters)); -void QMapboxGLPrivate::invalidate() -{ - if (!dirty) { - emit needsRendering(); - dirty = true; - } + requestRendering(); } -void QMapboxGLPrivate::render() +void QMapboxGLPrivate::setObserver(mbgl::RendererObserver &observer) { - frontend->render(); -} + m_rendererObserver = std::make_shared<QMapboxGLRendererObserver>( + *mbgl::util::RunLoop::Get(), observer); -void QMapboxGLPrivate::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) -{ - if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { - emit mapChanged(QMapboxGL::MapChangeRegionWillChange); - } else { - emit mapChanged(QMapboxGL::MapChangeRegionWillChangeAnimated); + std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex); + + if (m_mapRenderer) { + m_mapRenderer->setObserver(m_rendererObserver); } } -void QMapboxGLPrivate::onCameraIsChanging() +void QMapboxGLPrivate::createRenderer() { - emit mapChanged(QMapboxGL::MapChangeRegionIsChanging); -} + std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex); -void QMapboxGLPrivate::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) -{ - if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { - emit mapChanged(QMapboxGL::MapChangeRegionDidChange); - } else { - emit mapChanged(QMapboxGL::MapChangeRegionDidChangeAnimated); + if (m_mapRenderer) { + return; } -} -void QMapboxGLPrivate::onWillStartLoadingMap() -{ - emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); -} + m_mapRenderer = std::make_unique<QMapboxGLMapRenderer>( + m_pixelRatio, + *m_fileSourceObj, + *m_threadPool, + m_mode + ); -void QMapboxGLPrivate::onDidFinishLoadingMap() -{ - emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap); -} + connect(m_mapRenderer.get(), SIGNAL(needsRendering()), this, SLOT(requestRendering())); -void QMapboxGLPrivate::onDidFailLoadingMap(std::exception_ptr) -{ - emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); + m_mapRenderer->setObserver(m_rendererObserver); } -void QMapboxGLPrivate::onWillStartRenderingFrame() +void QMapboxGLPrivate::destroyRenderer() { - emit mapChanged(QMapboxGL::MapChangeWillStartRenderingFrame); -} + std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex); -void QMapboxGLPrivate::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) -{ - if (mode == mbgl::MapObserver::RenderMode::Partial) { - emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrame); - } else { - emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered); - } + m_mapRenderer.reset(); } -void QMapboxGLPrivate::onWillStartRenderingMap() +void QMapboxGLPrivate::render() { - emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); -} + std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex); -void QMapboxGLPrivate::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) -{ - if (mode == mbgl::MapObserver::RenderMode::Partial) { - emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMap); - } else { - emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMapFullyRendered); + if (!m_mapRenderer) { + createRenderer(); } -} -void QMapboxGLPrivate::onDidFinishLoadingStyle() -{ - emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingStyle); +#if defined(__APPLE__) && QT_VERSION < 0x050000 + // FIXME Qt 4.x provides an incomplete FBO at start. + // See https://bugreports.qt.io/browse/QTBUG-36802 for details. + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return; + } +#endif + + m_renderQueued.clear(); + m_mapRenderer->render(); } -void QMapboxGLPrivate::onSourceChanged(mbgl::style::Source&) +void QMapboxGLPrivate::setFramebufferObject(quint32 fbo, const QSize& size) { - std::string attribution; - for (const auto& source : mapObj->getStyle().getSources()) { - // Avoid duplicates by using the most complete attribution HTML snippet. - if (source->getAttribution() && (attribution.size() < source->getAttribution()->size())) - attribution = *source->getAttribution(); + std::lock_guard<std::recursive_mutex> lock(m_mapRendererMutex); + + if (!m_mapRenderer) { + createRenderer(); } - emit copyrightsChanged(QString::fromStdString(attribution)); - emit mapChanged(QMapboxGL::MapChangeSourceDidChange); -} -/*! - Initializes an OpenGL extension function such as Vertex Array Objects (VAOs), - required by Mapbox GL Native engine. -*/ -mbgl::gl::ProcAddress QMapboxGLPrivate::getExtensionFunctionPointer(const char* name) { -#if QT_VERSION >= 0x050000 - QOpenGLContext* thisContext = QOpenGLContext::currentContext(); - return thisContext->getProcAddress(name); -#else - const QGLContext* thisContext = QGLContext::currentContext(); - return reinterpret_cast<mbgl::gl::ProcAddress>(thisContext->getProcAddress(name)); -#endif + m_mapRenderer->updateFramebuffer(fbo, sanitizedSize(size)); } -void QMapboxGLPrivate::connectionEstablished() +void QMapboxGLPrivate::requestRendering() { - mbgl::NetworkStatus::Reachable(); + if (!m_renderQueued.test_and_set()) { + emit needsRendering(); + } } diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp new file mode 100644 index 0000000000..60c6b81841 --- /dev/null +++ b/platform/qt/src/qmapboxgl_map_observer.cpp @@ -0,0 +1,95 @@ +#include "qmapboxgl_map_observer.hpp" + +#include "qmapboxgl_p.hpp" + +QMapboxGLMapObserver::QMapboxGLMapObserver(QMapboxGLPrivate *d) + : d_ptr(d) +{ +} + +QMapboxGLMapObserver::~QMapboxGLMapObserver() +{ +} + +void QMapboxGLMapObserver::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) +{ + if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { + emit mapChanged(QMapboxGL::MapChangeRegionWillChange); + } else { + emit mapChanged(QMapboxGL::MapChangeRegionWillChangeAnimated); + } +} + +void QMapboxGLMapObserver::onCameraIsChanging() +{ + emit mapChanged(QMapboxGL::MapChangeRegionIsChanging); +} + +void QMapboxGLMapObserver::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) +{ + if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { + emit mapChanged(QMapboxGL::MapChangeRegionDidChange); + } else { + emit mapChanged(QMapboxGL::MapChangeRegionDidChangeAnimated); + } +} + +void QMapboxGLMapObserver::onWillStartLoadingMap() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); +} + +void QMapboxGLMapObserver::onDidFinishLoadingMap() +{ + emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap); +} + +void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr) +{ + emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); +} + +void QMapboxGLMapObserver::onWillStartRenderingFrame() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartRenderingFrame); +} + +void QMapboxGLMapObserver::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) +{ + if (mode == mbgl::MapObserver::RenderMode::Partial) { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrame); + } else { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered); + } +} + +void QMapboxGLMapObserver::onWillStartRenderingMap() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartRenderingMap); +} + +void QMapboxGLMapObserver::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) +{ + if (mode == mbgl::MapObserver::RenderMode::Partial) { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMap); + } else { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMapFullyRendered); + } +} + +void QMapboxGLMapObserver::onDidFinishLoadingStyle() +{ + emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingStyle); +} + +void QMapboxGLMapObserver::onSourceChanged(mbgl::style::Source&) +{ + std::string attribution; + for (const auto& source : d_ptr->mapObj->getStyle().getSources()) { + // Avoid duplicates by using the most complete attribution HTML snippet. + if (source->getAttribution() && (attribution.size() < source->getAttribution()->size())) + attribution = *source->getAttribution(); + } + emit copyrightsChanged(QString::fromStdString(attribution)); + emit mapChanged(QMapboxGL::MapChangeSourceDidChange); +} diff --git a/platform/qt/src/qmapboxgl_map_observer.hpp b/platform/qt/src/qmapboxgl_map_observer.hpp new file mode 100644 index 0000000000..c9d0581a90 --- /dev/null +++ b/platform/qt/src/qmapboxgl_map_observer.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "qmapboxgl.hpp" + +#include <mbgl/map/map_observer.hpp> +#include <mbgl/style/style.hpp> + +#include <QObject> + +#include <exception> +#include <memory> + +class QMapboxGLPrivate; + +class QMapboxGLMapObserver : public QObject, public mbgl::MapObserver +{ + Q_OBJECT + +public: + explicit QMapboxGLMapObserver(QMapboxGLPrivate *); + virtual ~QMapboxGLMapObserver(); + + // mbgl::MapObserver implementation. + void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) final; + void onCameraIsChanging() final; + void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) final; + void onWillStartLoadingMap() final; + void onDidFinishLoadingMap() final; + void onDidFailLoadingMap(std::exception_ptr) final; + void onWillStartRenderingFrame() final; + void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) final; + void onWillStartRenderingMap() final; + void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) final; + void onDidFinishLoadingStyle() final; + void onSourceChanged(mbgl::style::Source&) final; + +signals: + void mapChanged(QMapboxGL::MapChange); + void copyrightsChanged(const QString ©rightsHtml); + +private: + Q_DISABLE_COPY(QMapboxGLMapObserver) + + QMapboxGLPrivate *d_ptr; +}; diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp new file mode 100644 index 0000000000..7a9d1f6f78 --- /dev/null +++ b/platform/qt/src/qmapboxgl_map_renderer.cpp @@ -0,0 +1,86 @@ +#include "qmapboxgl_map_renderer.hpp" + +#include <QtGlobal> + +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) +{ +} + +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); + m_updateParameters = std::move(newParameters); +} + +void QMapboxGLMapRenderer::updateFramebuffer(quint32 fbo, const mbgl::Size &size) +{ + MBGL_VERIFY_THREAD(tid); + + m_backend.updateFramebuffer(fbo, size); +} + +void QMapboxGLMapRenderer::render() +{ + MBGL_VERIFY_THREAD(tid); + + std::shared_ptr<mbgl::UpdateParameters> params; + { + // Lock on the parameters + std::lock_guard<std::mutex> lock(m_updateMutex); + + if (!m_updateParameters) { + return; + } + + // Hold on to the update parameters during render + params = m_updateParameters; + } + + // 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(); + } + } +} + +void QMapboxGLMapRenderer::setObserver(std::shared_ptr<mbgl::RendererObserver> observer) +{ + m_renderer->setObserver(observer.get()); +} diff --git a/platform/qt/src/qmapboxgl_map_renderer.hpp b/platform/qt/src/qmapboxgl_map_renderer.hpp new file mode 100644 index 0000000000..adba11de51 --- /dev/null +++ b/platform/qt/src/qmapboxgl_map_renderer.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include "qmapboxgl.hpp" +#include "qmapboxgl_renderer_backend.hpp" + +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/renderer_observer.hpp> +#include <mbgl/storage/default_file_source.hpp> +#include <mbgl/util/shared_thread_pool.hpp> +#include <mbgl/util/util.hpp> + +#include <QtGlobal> + +#include <memory> +#include <mutex> +#include <queue> + +namespace mbgl { +class Renderer; +class UpdateParameters; +} // namespace mbgl + +class QMapboxGLRendererBackend; + +class QMapboxGLMapRenderer : public QObject, public mbgl::Scheduler +{ + Q_OBJECT + +public: + QMapboxGLMapRenderer(qreal pixelRatio, mbgl::DefaultFileSource &, + 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>); + + // Thread-safe, called by the Frontend + void updateParameters(std::shared_ptr<mbgl::UpdateParameters>); + +signals: + void needsRendering(); + +private: + MBGL_STORE_THREAD(tid) + + Q_DISABLE_COPY(QMapboxGLMapRenderer) + + std::mutex m_updateMutex; + std::shared_ptr<mbgl::UpdateParameters> m_updateParameters; + + 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; +}; diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index f947c09f48..51c7cb8fc4 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -1,22 +1,24 @@ #pragma once #include "qmapboxgl.hpp" -#include "qmapboxgl_renderer_frontend_p.hpp" +#include "qmapboxgl_map_observer.hpp" +#include "qmapboxgl_map_renderer.hpp" #include <mbgl/actor/actor.hpp> #include <mbgl/map/map.hpp> -#include <mbgl/renderer/renderer_backend.hpp> -#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/renderer/renderer_frontend.hpp> #include <mbgl/storage/default_file_source.hpp> -#include <mbgl/util/geo.hpp> #include <mbgl/storage/resource_transform.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/geo.hpp> #include <QObject> #include <QSize> +#include <atomic> #include <memory> -class QMapboxGLPrivate : public QObject, public mbgl::RendererBackend, public mbgl::MapObserver +class QMapboxGLPrivate : public QObject, public mbgl::RendererFrontend { Q_OBJECT @@ -24,55 +26,40 @@ public: explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio); virtual ~QMapboxGLPrivate(); + // mbgl::RendererFrontend implementation. + void reset() final {} + void setObserver(mbgl::RendererObserver &) final; + void update(std::shared_ptr<mbgl::UpdateParameters>) final; - // mbgl::RendererBackend implementation. - void bind() final; - mbgl::Size getFramebufferSize() const final; - void updateAssumedState() final; - void activate() final {} - void deactivate() final {} - - // mbgl::MapObserver implementation. - void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) final; - void onCameraIsChanging() final; - void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) final; - void onWillStartLoadingMap() final; - void onDidFinishLoadingMap() final; - void onDidFailLoadingMap(std::exception_ptr) final; - void onWillStartRenderingFrame() final; - void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) final; - void onWillStartRenderingMap() final; - void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) final; - void onDidFinishLoadingStyle() final; - void onSourceChanged(mbgl::style::Source&) final; + // These need to be called on the same thread. + void createRenderer(); + void destroyRenderer(); + void render(); + void setFramebufferObject(quint32 fbo, const QSize& size); mbgl::EdgeInsets margins; - QSize size { 0, 0 }; - QSize fbSize { 0, 0 }; - quint32 fbObject = 0; - - QMapboxGL *q_ptr { nullptr }; - - std::shared_ptr<mbgl::DefaultFileSource> fileSourceObj; - std::shared_ptr<mbgl::ThreadPool> threadPool; - std::unique_ptr<QMapboxGLRendererFrontend> frontend; std::unique_ptr<mbgl::Map> mapObj; - bool dirty { false }; - -private: - mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) override; - public slots: - void connectionEstablished(); - void invalidate(); - void render(); + void requestRendering(); signals: void needsRendering(); - void mapChanged(QMapboxGL::MapChange); - void copyrightsChanged(const QString ©rightsHtml); private: - std::unique_ptr< mbgl::Actor<mbgl::ResourceTransform> > m_resourceTransform; + Q_DISABLE_COPY(QMapboxGLPrivate) + + std::recursive_mutex m_mapRendererMutex; + std::shared_ptr<mbgl::RendererObserver> m_rendererObserver; + + std::unique_ptr<QMapboxGLMapObserver> m_mapObserver; + std::shared_ptr<mbgl::DefaultFileSource> m_fileSourceObj; + std::shared_ptr<mbgl::ThreadPool> m_threadPool; + std::unique_ptr<QMapboxGLMapRenderer> m_mapRenderer; + std::unique_ptr<mbgl::Actor<mbgl::ResourceTransform>> m_resourceTransform; + + QMapboxGLSettings::GLContextMode m_mode; + qreal m_pixelRatio; + + std::atomic_flag m_renderQueued = ATOMIC_FLAG_INIT; }; diff --git a/platform/qt/src/qmapboxgl_renderer_backend.cpp b/platform/qt/src/qmapboxgl_renderer_backend.cpp new file mode 100644 index 0000000000..917741f5ce --- /dev/null +++ b/platform/qt/src/qmapboxgl_renderer_backend.cpp @@ -0,0 +1,49 @@ +#include "qmapboxgl_renderer_backend.hpp" + +#include <QtGlobal> + +#if QT_VERSION >= 0x050000 +#include <QOpenGLContext> +#else +#include <QGLContext> +#endif + +void QMapboxGLRendererBackend::updateAssumedState() +{ + assumeFramebufferBinding(ImplicitFramebufferBinding); + assumeViewport(0, 0, m_size); +} + +void QMapboxGLRendererBackend::bind() +{ + assert(mbgl::BackendScope::exists()); + + setFramebufferBinding(m_fbo); + setViewport(0, 0, m_size); +} + +mbgl::Size QMapboxGLRendererBackend::getFramebufferSize() const +{ + return m_size; +} + +void QMapboxGLRendererBackend::updateFramebuffer(quint32 fbo, const mbgl::Size &size) +{ + m_fbo = fbo; + m_size = size; +} + +/*! + Initializes an OpenGL extension function such as Vertex Array Objects (VAOs), + required by Mapbox GL Native engine. +*/ +mbgl::gl::ProcAddress QMapboxGLRendererBackend::getExtensionFunctionPointer(const char* name) +{ +#if QT_VERSION >= 0x050000 + QOpenGLContext* thisContext = QOpenGLContext::currentContext(); + return thisContext->getProcAddress(name); +#else + const QGLContext* thisContext = QGLContext::currentContext(); + return reinterpret_cast<mbgl::gl::ProcAddress>(thisContext->getProcAddress(name)); +#endif +} diff --git a/platform/qt/src/qmapboxgl_renderer_backend.hpp b/platform/qt/src/qmapboxgl_renderer_backend.hpp new file mode 100644 index 0000000000..de66b035fc --- /dev/null +++ b/platform/qt/src/qmapboxgl_renderer_backend.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "qmapboxgl.hpp" + +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/storage/default_file_source.hpp> +#include <mbgl/util/shared_thread_pool.hpp> + +class QMapboxGLRendererBackend : public mbgl::RendererBackend +{ +public: + QMapboxGLRendererBackend() = default; + virtual ~QMapboxGLRendererBackend() = default; + + // mbgl::RendererBackend implementation + void updateAssumedState() final; + void bind() final; + mbgl::Size getFramebufferSize() const final; + + void updateFramebuffer(quint32 fbo, const mbgl::Size &); + +protected: + mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) final; + + // No-op, implicit mode. + void activate() final {} + void deactivate() final {} + +private: + quint32 m_fbo = 0; + mbgl::Size m_size = { 0, 0 }; + + Q_DISABLE_COPY(QMapboxGLRendererBackend) +}; diff --git a/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp b/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp deleted file mode 100644 index ea60851eb4..0000000000 --- a/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "qmapboxgl_renderer_frontend_p.hpp" - -#include <mbgl/renderer/backend_scope.hpp> -#include <mbgl/renderer/renderer.hpp> - -QMapboxGLRendererFrontend::QMapboxGLRendererFrontend(std::unique_ptr<mbgl::Renderer> renderer_, mbgl::RendererBackend& backend_) - : renderer(std::move(renderer_)) - , backend(backend_) { -} - -QMapboxGLRendererFrontend::~QMapboxGLRendererFrontend() = default; - -void QMapboxGLRendererFrontend::reset() { - if (renderer) { - renderer.reset(); - } -} - -void QMapboxGLRendererFrontend::update(std::shared_ptr<mbgl::UpdateParameters> updateParameters_) { - updateParameters = updateParameters_; - emit updated(); -} - -void QMapboxGLRendererFrontend::setObserver(mbgl::RendererObserver& observer_) { - if (!renderer) return; - - renderer->setObserver(&observer_); -} - -void QMapboxGLRendererFrontend::render() { - if (!renderer || !updateParameters) return; - - // The OpenGL implementation automatically enables the OpenGL context for us. - mbgl::BackendScope scope { backend, mbgl::BackendScope::ScopeType::Implicit }; - - renderer->render(*updateParameters); -} diff --git a/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp b/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp deleted file mode 100644 index c5e2bacc34..0000000000 --- a/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include <mbgl/renderer/renderer_backend.hpp> -#include <mbgl/renderer/renderer_frontend.hpp> - -#include <QObject> - -namespace mbgl { - class Renderer; -} // namespace mbgl - -class QMapboxGLRendererFrontend : public QObject, public mbgl::RendererFrontend -{ - Q_OBJECT - -public: - explicit QMapboxGLRendererFrontend(std::unique_ptr<mbgl::Renderer>, mbgl::RendererBackend&); - ~QMapboxGLRendererFrontend() override; - - void reset() override; - void setObserver(mbgl::RendererObserver&) override; - - void update(std::shared_ptr<mbgl::UpdateParameters>) override; - -public slots: - void render(); - -signals: - void updated(); - -private: - std::unique_ptr<mbgl::Renderer> renderer; - mbgl::RendererBackend& backend; - std::shared_ptr<mbgl::UpdateParameters> updateParameters; -}; diff --git a/platform/qt/src/qmapboxgl_renderer_observer.hpp b/platform/qt/src/qmapboxgl_renderer_observer.hpp new file mode 100644 index 0000000000..ee340113ff --- /dev/null +++ b/platform/qt/src/qmapboxgl_renderer_observer.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <mbgl/actor/actor.hpp> +#include <mbgl/actor/actor_ref.hpp> +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/renderer/renderer_observer.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <memory> + +// Forwards RendererObserver signals to the given +// Delegate RendererObserver on the given RunLoop +class QMapboxGLRendererObserver : public mbgl::RendererObserver { +public: + QMapboxGLRendererObserver(mbgl::util::RunLoop& mapRunLoop, mbgl::RendererObserver& delegate_) + : mailbox(std::make_shared<mbgl::Mailbox>(mapRunLoop)) + , delegate(delegate_, mailbox) { + } + + ~QMapboxGLRendererObserver() { + mailbox->close(); + } + + void onInvalidate() final { + delegate.invoke(&mbgl::RendererObserver::onInvalidate); + } + + void onResourceError(std::exception_ptr err) final { + delegate.invoke(&mbgl::RendererObserver::onResourceError, err); + } + + void onWillStartRenderingMap() final { + delegate.invoke(&mbgl::RendererObserver::onWillStartRenderingMap); + } + + void onWillStartRenderingFrame() final { + delegate.invoke(&mbgl::RendererObserver::onWillStartRenderingFrame); + } + + void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) final { + delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded); + } + + void onDidFinishRenderingMap() final { + delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingMap); + } + +private: + std::shared_ptr<mbgl::Mailbox> mailbox; + mbgl::ActorRef<mbgl::RendererObserver> delegate; +}; diff --git a/platform/qt/src/qt_logging.cpp b/platform/qt/src/qt_logging.cpp index acbe9562d0..acbe9562d0 100755..100644 --- a/platform/qt/src/qt_logging.cpp +++ b/platform/qt/src/qt_logging.cpp diff --git a/platform/qt/src/run_loop.cpp b/platform/qt/src/run_loop.cpp index af0c50ebb9..c25243c8e7 100644 --- a/platform/qt/src/run_loop.cpp +++ b/platform/qt/src/run_loop.cpp @@ -52,11 +52,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() { return nullptr; } -void RunLoop::push(std::shared_ptr<WorkTask> task) { - withMutex([&] { - queue.push(std::move(task)); - impl->async->send(); - }); +void RunLoop::wake() { + impl->async->send(); } void RunLoop::run() { diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp index 80efd6a326..4bcaea0e31 100644 --- a/platform/qt/src/sqlite3.cpp +++ b/platform/qt/src/sqlite3.cpp @@ -24,11 +24,11 @@ namespace mapbox { namespace sqlite { // https://www.sqlite.org/rescode.html#ok -static_assert(mbgl::underlying_type(Exception::OK) == 0, "error"); +static_assert(mbgl::underlying_type(ResultCode::OK) == 0, "error"); // https://www.sqlite.org/rescode.html#cantopen -static_assert(mbgl::underlying_type(Exception::CANTOPEN) == 14, "error"); +static_assert(mbgl::underlying_type(ResultCode::CantOpen) == 14, "error"); // https://www.sqlite.org/rescode.html#notadb -static_assert(mbgl::underlying_type(Exception::NOTADB) == 26, "error"); +static_assert(mbgl::underlying_type(ResultCode::NotADB) == 26, "error"); void checkQueryError(const QSqlQuery& query) { QSqlError lastError = query.lastError(); @@ -57,7 +57,7 @@ void checkDatabaseOpenError(const QSqlDatabase &db) { // always returns -1 for `nativeErrorCode()` on database errors. QSqlError lastError = db.lastError(); if (lastError.type() != QSqlError::NoError) { - throw Exception { Exception::Code::CANTOPEN, "Error opening the database." }; + throw Exception { ResultCode::CantOpen, "Error opening the database." }; } } @@ -74,7 +74,7 @@ public: : connectionName(QString::number(uint64_t(QThread::currentThread())) + incrementCounter()) { if (!QSqlDatabase::drivers().contains("QSQLITE")) { - throw Exception { Exception::Code::CANTOPEN, "SQLite driver not found." }; + throw Exception { ResultCode::CantOpen, "SQLite driver not found." }; } assert(!QSqlDatabase::contains(connectionName)); @@ -186,74 +186,82 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - return Statement(this, query); -} - -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique<StatementImpl>(QString(sql), QSqlDatabase::database(db->impl->connectionName))) { +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique<StatementImpl>(QString(sql), + QSqlDatabase::database(db.impl->connectionName))) { assert(impl); } -Statement::Statement(Statement &&other) - : impl(std::move(other.impl)) { - assert(impl); +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement &Statement::operator=(Statement &&other) { - assert(impl); - std::swap(impl, other.impl); - return *this; +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); + +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() { +Query::~Query() { + reset(); + clearBindings(); + +#ifndef NDEBUG + stmt.used = false; +#endif } -template void Statement::bind(int, int64_t); +template void Query::bind(int, int64_t); template <typename T> -void Statement::bind(int offset, T value) { - assert(impl); +void Query::bind(int offset, T value) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, std::nullptr_t) { - assert(impl); +void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, int32_t value) { +void Query::bind(int offset, int32_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, bool value) { +void Query::bind(int offset, bool value) { bind(offset, static_cast<int>(value)); } template <> -void Statement::bind(int offset, int8_t value) { +void Query::bind(int offset, int8_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, uint8_t value) { +void Query::bind(int offset, uint8_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, mbgl::Timestamp value) { +void Query::bind(int offset, mbgl::Timestamp value) { bind(offset, std::chrono::system_clock::to_time_t(value)); } template <> -void Statement::bind(int offset, optional<std::string> value) { +void Query::bind(int offset, optional<std::string> value) { if (value) { bind(offset, *value); } else { @@ -262,7 +270,7 @@ void Statement::bind(int offset, optional<std::string> value) { } template <> -void Statement::bind(int offset, optional<mbgl::Timestamp> value) { +void Query::bind(int offset, optional<mbgl::Timestamp> value) { if (value) { bind(offset, *value); } else { @@ -270,30 +278,25 @@ void Statement::bind(int offset, optional<mbgl::Timestamp> value) { } } -void Statement::bind(int offset, const char* value, std::size_t length, bool retain) { - assert(impl); +void Query::bind(int offset, const char* value, std::size_t length, bool /* retain */) { + assert(stmt.impl); if (length > std::numeric_limits<int>::max()) { // Kept for consistence with the default implementation. throw std::range_error("value too long"); } - // Qt SQLite driver treats QByteArray as blob: we need to explicitly - // declare the variant type as string. - QVariant text(QVariant::Type::String); - text.setValue(retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length)); - // Field numbering starts at 0. - impl->query.bindValue(offset - 1, std::move(text), QSql::In); + stmt.impl->query.bindValue(offset - 1, QString(QByteArray(value, length)), QSql::In); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { + assert(stmt.impl); const char* value = reinterpret_cast<const char*>(value_); if (length > std::numeric_limits<int>::max()) { // Kept for consistence with the default implementation. @@ -301,123 +304,123 @@ void Statement::bindBlob(int offset, const void* value_, std::size_t length, boo } // Field numbering starts at 0. - impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : + stmt.impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length), QSql::In | QSql::Binary); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { +void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } -bool Statement::run() { - assert(impl); +bool Query::run() { + assert(stmt.impl); - if (!impl->query.isValid()) { - if (impl->query.exec()) { - impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>(); - impl->changes = impl->query.numRowsAffected(); + if (!stmt.impl->query.isValid()) { + if (stmt.impl->query.exec()) { + stmt.impl->lastInsertRowId = stmt.impl->query.lastInsertId().value<int64_t>(); + stmt.impl->changes = stmt.impl->query.numRowsAffected(); } else { - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } } - const bool hasNext = impl->query.next(); - if (!hasNext) impl->query.finish(); + const bool hasNext = stmt.impl->query.next(); + if (!hasNext) stmt.impl->query.finish(); return hasNext; } -template bool Statement::get(int); -template int Statement::get(int); -template int64_t Statement::get(int); -template double Statement::get(int); +template bool Query::get(int); +template int Query::get(int); +template int64_t Query::get(int); +template double Query::get(int); -template <typename T> T Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <typename T> T Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return value.value<T>(); } -template <> std::vector<uint8_t> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray byteArray = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::vector<uint8_t> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray byteArray = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); std::vector<uint8_t> blob(byteArray.begin(), byteArray.end()); return blob; } -template <> mbgl::Timestamp Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> mbgl::Timestamp Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return std::chrono::time_point_cast<std::chrono::seconds>( std::chrono::system_clock::from_time_t(value.value<::time_t>())); } -template <> optional<int64_t> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<int64_t> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value<int64_t>() }; } -template <> optional<double> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<double> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value<double>() }; } -template <> std::string Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::string Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); return std::string(value.constData(), value.size()); } -template <> optional<std::string> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> optional<std::string> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::string(value.constData(), value.size()) }; } -template <> optional<mbgl::Timestamp> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<mbgl::Timestamp> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::chrono::time_point_cast<mbgl::Seconds>( std::chrono::system_clock::from_time_t(value.value<::time_t>())) }; } -void Statement::reset() { - assert(impl); - impl->query.finish(); +void Query::reset() { + assert(stmt.impl); + stmt.impl->query.finish(); } -void Statement::clearBindings() { +void Query::clearBindings() { // no-op } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - return (impl->changes < 0 ? 0 : impl->changes); +uint64_t Query::changes() const { + assert(stmt.impl); + return (stmt.impl->changes < 0 ? 0 : stmt.impl->changes); } Transaction::Transaction(Database& db_, Mode mode) diff --git a/platform/qt/test/qmapboxgl.test.cpp b/platform/qt/test/qmapboxgl.test.cpp index 932460b932..2a56b346a3 100644 --- a/platform/qt/test/qmapboxgl.test.cpp +++ b/platform/qt/test/qmapboxgl.test.cpp @@ -15,8 +15,8 @@ QMapboxGLTest::QMapboxGLTest() : size(512, 512), fbo((assert(widget.context()->i this, SLOT(onMapChanged(QMapboxGL::MapChange))); connect(&map, SIGNAL(needsRendering()), this, SLOT(onNeedsRendering())); - map.resize(fbo.size(), fbo.size()); - map.setFramebufferObject(fbo.handle()); + map.resize(fbo.size()); + map.setFramebufferObject(fbo.handle(), fbo.size()); map.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14); } |