From d055098540df99a5d426360e9322c659e678e5ee Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Wed, 27 Nov 2019 15:37:07 +0100 Subject: Enable mercator-to-screen projection in GLSL With this change, all the geo-to-screen conversion, and the triangulation operations for geo polylines and geo polygon are performed either at set time or in the shader. A separate bounding box geometry is processed in the old way to provide a correct QtQuick Item geometry, that can be used for nesting mouse areas, performing translations, input event delivery, etc. With this approach, performance are improved by more than one order of magnitude in average, but complex geometries will of course benefit more. It also adds correct rendering support for polygons with holes, previously only rendered correctly by the MapboxGL plugin. The polyline shader has basic miter joins. The miter is skipped if the angle is too sharp to avoid complicating the implementation. This shader introduces some glitches when the polyline is minified, for which the real fix is to have LOD for the geometry, and render simplified geometries at low zoom levels (added in a subsequent patch). Note: this approach, at least in its current implementation, does not support enabling layers on individual items, only on the Map element. Task-number: QTBUG-49303 Task-number: QTBUG-38459 Change-Id: I0c2dc0bf364d32f74ca7c4014f6d66e6219c8ae4 Reviewed-by: Alexandru Croitor --- src/location/labs/qsg/qmapcircleobjectqsg.cpp | 6 +++--- src/location/labs/qsg/qmapcircleobjectqsg_p_p.h | 2 +- src/location/labs/qsg/qmappolygonobjectqsg_p_p.h | 2 +- src/location/labs/qsg/qqsgmapobject_p.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/location/labs') diff --git a/src/location/labs/qsg/qmapcircleobjectqsg.cpp b/src/location/labs/qsg/qmapcircleobjectqsg.cpp index 32f3030b..f79be136 100644 --- a/src/location/labs/qsg/qmapcircleobjectqsg.cpp +++ b/src/location/labs/qsg/qmapcircleobjectqsg.cpp @@ -66,7 +66,7 @@ void QMapCircleObjectPrivateQSG::updateCirclePath() { const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); QList path; - QDeclarativeCircleMapItem::calculatePeripheralPoints(path, center(), radius(), CircleSamples, m_leftBound); + QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, center(), radius(), CircleSamples, m_leftBound); m_circlePath.clear(); for (const QGeoCoordinate &c : path) m_circlePath << p.geoToMapProjection(c); @@ -86,7 +86,7 @@ void QMapCircleObjectPrivateQSG::updateGeometry() QList circlePath = m_circlePath; int pathCount = circlePath.size(); - bool preserve = QDeclarativeCircleMapItem::preserveCircleGeometry(circlePath, center(), radius(), p); + bool preserve = QDeclarativeCircleMapItemPrivateCPU::preserveCircleGeometry(circlePath, center(), radius(), p); // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft()); // to fix QTBUG-62154 m_geometry.markSourceDirty(); @@ -94,7 +94,7 @@ void QMapCircleObjectPrivateQSG::updateGeometry() m_geometry.setPreserveGeometry(preserve, m_leftBound); bool invertedCircle = false; - if (QDeclarativeCircleMapItem::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) { + if (QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) { m_geometry.updateScreenPointsInvert(circlePath, *m_map); // invert fill area for really huge circles invertedCircle = true; } else { diff --git a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h index 4e8162fa..6bbeb397 100644 --- a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h @@ -50,7 +50,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h index 0f42a92e..8e6ae8a8 100644 --- a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h @@ -52,7 +52,7 @@ #include #include #include -#include +#include #include QT_BEGIN_NAMESPACE diff --git a/src/location/labs/qsg/qqsgmapobject_p.h b/src/location/labs/qsg/qqsgmapobject_p.h index 52036805..0a717e62 100644 --- a/src/location/labs/qsg/qqsgmapobject_p.h +++ b/src/location/labs/qsg/qqsgmapobject_p.h @@ -51,7 +51,7 @@ #include #include #include -#include +#include QT_BEGIN_NAMESPACE -- cgit v1.2.1 From e82c41d35ddd6ef0d14e1d01ea1dfd46742bc0fe Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Thu, 26 Dec 2019 21:31:03 +0100 Subject: Fix Map*ObjectsQSG implementation triggering QSGBatchRenderer crashes The new approach introduces a root node for all objects, that is repopulated at every repaint. Change-Id: I4562e1aaa18999a03e8c38fe3bf59fe41f14dd70 Reviewed-by: Alex Blasche --- src/location/labs/qsg/qgeomapobjectqsgsupport.cpp | 12 +++++++++--- src/location/labs/qsg/qgeomapobjectqsgsupport_p.h | 3 ++- src/location/labs/qsg/qmapcircleobjectqsg.cpp | 14 +++++++++----- src/location/labs/qsg/qmapiconobjectqsg.cpp | 5 +---- src/location/labs/qsg/qmappolygonobjectqsg.cpp | 14 ++++++++------ src/location/labs/qsg/qmappolylineobjectqsg.cpp | 11 ++++++----- 6 files changed, 35 insertions(+), 24 deletions(-) (limited to 'src/location/labs') diff --git a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp index 1a1b102b..6cb2c44a 100644 --- a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp +++ b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp @@ -154,10 +154,15 @@ void QGeoMapObjectQSGSupport::removeMapObject(QGeoMapObject *obj) void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *window) { + if (!m_mapObjectsRootNode) { + m_mapObjectsRootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); + root->appendChildNode(m_mapObjectsRootNode); + } + + m_mapObjectsRootNode->removeAllChildNodes(); for (int i = 0; i < m_removedMapObjects.size(); ++i) { MapObject mo = m_removedMapObjects[i]; if (mo.qsgNode) { - root->removeChildNode(mo.qsgNode); delete mo.qsgNode; mo.qsgNode = nullptr; // mo.sgObject is now invalid as it is destroyed right after appending @@ -176,7 +181,7 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind MapObject &mo = m_mapObjects[i]; QQSGMapObject *sgo = mo.sgObject; QSGNode *oldNode = mo.qsgNode; - mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, root, window); + mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, m_mapObjectsRootNode, window); if (Q_UNLIKELY(!mo.qsgNode)) { qWarning() << "updateMapObjectNode for "<type() << " returned NULL"; } else if (mo.visibleNode && (mo.visibleNode->visible() != mo.object->visible())) { @@ -192,7 +197,7 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind QQSGMapObject *sgo = mo.sgObject; QSGNode *oldNode = mo.qsgNode; sgo->updateGeometry(); // or subtree will be blocked - mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, root, window); + mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, m_mapObjectsRootNode, window); if (mo.qsgNode) { if (mo.visibleNode && (mo.visibleNode->visible() != mo.object->visible())) { mo.visibleNode->setVisible(mo.object->visible()); @@ -208,6 +213,7 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind for (int i: qAsConst(toRemove)) m_pendingMapObjects.removeAt(i); + m_mapObjectsRootNode->setSubtreeBlocked(false); } void QGeoMapObjectQSGSupport::updateObjectsGeometry() diff --git a/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h b/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h index bb0477c5..1ec966fa 100644 --- a/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h +++ b/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h @@ -57,10 +57,10 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE - struct Q_LOCATION_PRIVATE_EXPORT MapObject { MapObject(QPointer &o, QQSGMapObject *sgo) : object(o), sgObject(sgo) {} @@ -85,6 +85,7 @@ public: QList m_pendingMapObjects; QList m_removedMapObjects; QGeoMap *m_map = nullptr; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_mapObjectsRootNode = nullptr; }; QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmapcircleobjectqsg.cpp b/src/location/labs/qsg/qmapcircleobjectqsg.cpp index f79be136..6c69ce5a 100644 --- a/src/location/labs/qsg/qmapcircleobjectqsg.cpp +++ b/src/location/labs/qsg/qmapcircleobjectqsg.cpp @@ -154,15 +154,16 @@ QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, // Q_UNUSED(visibleNode); // coz of -Werror=unused-but-set-parameter MapPolygonNode *node = static_cast(oldNode); - bool created = false; if (!node) { + if (!m_geometry.size() && !m_borderGeometry.size()) { + return nullptr; + } node = new MapPolygonNode(); *visibleNode = static_cast(node); - created = true; } //TODO: update only material - if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode || created) { + if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode) { //QMapPolygonObject *p = static_cast(q); node->update(color(), borderColor(), &m_geometry, &m_borderGeometry); m_geometry.setPreserveGeometry(false); @@ -171,9 +172,12 @@ QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, m_borderGeometry.markClean(); } - if (created) + if (m_geometry.size() || m_borderGeometry.size()) { root->appendChildNode(node); - + } else { + delete node; + return nullptr; + } return node; } diff --git a/src/location/labs/qsg/qmapiconobjectqsg.cpp b/src/location/labs/qsg/qmapiconobjectqsg.cpp index d9a80c91..10948d82 100644 --- a/src/location/labs/qsg/qmapiconobjectqsg.cpp +++ b/src/location/labs/qsg/qmapiconobjectqsg.cpp @@ -102,7 +102,6 @@ QSGNode *QMapIconObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, QQuickWindow *window) { Q_UNUSED(visibleNode); - bool created = false; RootNode *node = static_cast(oldNode); if (!node) { node = new RootNode(); @@ -110,7 +109,6 @@ QSGNode *QMapIconObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, m_imageNode->setOwnsTexture(true); node->appendChildNode(m_imageNode); *visibleNode = static_cast(node); - created = true; } if (m_imageDirty) { @@ -131,8 +129,7 @@ QSGNode *QMapIconObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, } } - if (created) - root->appendChildNode(node); + root->appendChildNode(node); return node; } diff --git a/src/location/labs/qsg/qmappolygonobjectqsg.cpp b/src/location/labs/qsg/qmappolygonobjectqsg.cpp index 9963cac9..25473478 100644 --- a/src/location/labs/qsg/qmappolygonobjectqsg.cpp +++ b/src/location/labs/qsg/qmappolygonobjectqsg.cpp @@ -85,17 +85,16 @@ QSGNode *QMapPolygonObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, Q_UNUSED(visibleNode); MapPolygonNode *node = static_cast(oldNode); - bool created = false; if (!node) { - if (!m_geometry.size() && !m_borderGeometry.size()) + if (!m_geometry.size() && !m_borderGeometry.size()) { return nullptr; + } node = new MapPolygonNode(); *visibleNode = static_cast(node); - created = true; } //TODO: update only material - if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode || created) { + if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode) { node->update(fillColor(), borderColor(), &m_geometry, &m_borderGeometry); m_geometry.setPreserveGeometry(false); m_borderGeometry.setPreserveGeometry(false); @@ -103,9 +102,12 @@ QSGNode *QMapPolygonObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, m_borderGeometry.markClean(); } - if (created) + if (m_geometry.size() || m_borderGeometry.size()) { root->appendChildNode(node); - + } else { + delete node; + return nullptr; + } return node; } diff --git a/src/location/labs/qsg/qmappolylineobjectqsg.cpp b/src/location/labs/qsg/qmappolylineobjectqsg.cpp index 2bf5b287..96f66565 100644 --- a/src/location/labs/qsg/qmappolylineobjectqsg.cpp +++ b/src/location/labs/qsg/qmappolylineobjectqsg.cpp @@ -107,25 +107,26 @@ QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, Q_UNUSED(visibleNode); MapPolylineNode *node = static_cast(oldNode); - bool created = false; if (!node) { if (!m_geometry.size()) // condition to block the subtree return nullptr; node = new MapPolylineNode(); *visibleNode = static_cast(node); - created = true; } //TODO: update only material - if (m_geometry.isScreenDirty() || !oldNode || created) { + if (m_geometry.isScreenDirty() || !oldNode) { node->update(color(), &m_geometry); m_geometry.setPreserveGeometry(false); m_geometry.markClean(); } - if (created) + if (m_geometry.size()) { root->appendChildNode(node); - + } else { + delete node; + return nullptr; + } return node; } -- cgit v1.2.1 From a4469cad4041f21e640efa9ca5d0b192dd702955 Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Fri, 27 Dec 2019 22:39:47 +0100 Subject: Port Map*ObjectPrivateQSG to the shader-based projection geometries This patch changes the implementation of the scenegraph-based mapobjects (those created by QGeoTiledMap) to be the recently introduced one sporting shader-based map projection. This is much faster than the previous, but may introduce small glitches at this time (mostly on minified polylines). Adding polyline LOD simplification will solve this problem. Compared to equivalent Map Items (that is, Map Items backed by the same underlying implementation), map objects now are approximately 1.5x faster. This measure has been gathered using medium-size polygons (contained in the file 10_countries.json from the geojson_viewer example). The difference is caused by the additional QtQuick geometry related operations (essentially projecting the bounding box for each item) present in MapItems. Smaller polygons may therefore yield larger improvements, larger polygons the opposite. Change-Id: I3fc92b02b74a3a4d001e69755949a98f80d8a3d3 Reviewed-by: Alex Blasche --- src/location/labs/qmappolygonobject.cpp | 3 +- src/location/labs/qmappolylineobject.cpp | 6 +- src/location/labs/qmappolylineobject_p_p.h | 3 +- src/location/labs/qsg/qgeomapobjectqsgsupport.cpp | 2 + src/location/labs/qsg/qmapcircleobjectqsg.cpp | 266 +++++++++++++++++----- src/location/labs/qsg/qmapcircleobjectqsg_p_p.h | 40 +++- src/location/labs/qsg/qmappolygonobjectqsg.cpp | 186 +++++++-------- src/location/labs/qsg/qmappolygonobjectqsg_p_p.h | 11 +- src/location/labs/qsg/qmappolylineobjectqsg.cpp | 91 +++++--- src/location/labs/qsg/qmappolylineobjectqsg_p_p.h | 7 +- 10 files changed, 420 insertions(+), 195 deletions(-) (limited to 'src/location/labs') diff --git a/src/location/labs/qmappolygonobject.cpp b/src/location/labs/qmappolygonobject.cpp index 5a8ce228..9d679f31 100644 --- a/src/location/labs/qmappolygonobject.cpp +++ b/src/location/labs/qmappolygonobject.cpp @@ -145,10 +145,9 @@ void QMapPolygonObjectPrivateDefault::setGeoShape(const QGeoShape &shape) return; const QGeoPolygon poly(shape); - setPath(poly.path()); // to handle overrides for (int i = 0; i < poly.holesCount(); i++) m_path.addHole(poly.holePath(i)); - emit static_cast(q)->pathChanged(); + setPath(poly.path()); // to handle overrides. Last as it normally emits static_cast(q)->pathChanged(); } bool QMapPolygonObjectPrivate::equals(const QGeoMapObjectPrivate &other) const diff --git a/src/location/labs/qmappolylineobject.cpp b/src/location/labs/qmappolylineobject.cpp index 81390d7c..acfe6609 100644 --- a/src/location/labs/qmappolylineobject.cpp +++ b/src/location/labs/qmappolylineobject.cpp @@ -76,7 +76,7 @@ QMapPolylineObjectPrivateDefault::QMapPolylineObjectPrivateDefault(QGeoMapObject QMapPolylineObjectPrivateDefault::QMapPolylineObjectPrivateDefault(const QMapPolylineObjectPrivate &other) : QMapPolylineObjectPrivate(other.q) { - m_path = other.path(); + m_path.setPath(other.path()); m_color = other.color(); m_width = other.width(); } @@ -88,12 +88,12 @@ QMapPolylineObjectPrivateDefault::~QMapPolylineObjectPrivateDefault() QList QMapPolylineObjectPrivateDefault::path() const { - return m_path; + return m_path.path(); } void QMapPolylineObjectPrivateDefault::setPath(const QList &path) { - m_path = path; + m_path.setPath(path); } QColor QMapPolylineObjectPrivateDefault::color() const diff --git a/src/location/labs/qmappolylineobject_p_p.h b/src/location/labs/qmappolylineobject_p_p.h index a0eb3711..3fdf1a1f 100644 --- a/src/location/labs/qmappolylineobject_p_p.h +++ b/src/location/labs/qmappolylineobject_p_p.h @@ -51,6 +51,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -95,7 +96,7 @@ public: QGeoMapObjectPrivate *clone() override; public: - QList m_path; + QGeoPath m_path; // small overhead compared to plain QList QColor m_color; qreal m_width = 0; diff --git a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp index 6cb2c44a..f965efb7 100644 --- a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp +++ b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp @@ -152,6 +152,7 @@ void QGeoMapObjectQSGSupport::removeMapObject(QGeoMapObject *obj) } } +// called in the render thread void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *window) { if (!m_mapObjectsRootNode) { @@ -216,6 +217,7 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind m_mapObjectsRootNode->setSubtreeBlocked(false); } +// called in GUI thread void QGeoMapObjectQSGSupport::updateObjectsGeometry() { for (int i = 0; i < m_mapObjects.size(); ++i) { diff --git a/src/location/labs/qsg/qmapcircleobjectqsg.cpp b/src/location/labs/qsg/qmapcircleobjectqsg.cpp index 6c69ce5a..5b1f5361 100644 --- a/src/location/labs/qsg/qmapcircleobjectqsg.cpp +++ b/src/location/labs/qsg/qmapcircleobjectqsg.cpp @@ -41,16 +41,19 @@ QT_BEGIN_NAMESPACE static const int CircleSamples = 128; QMapCircleObjectPrivateQSG::QMapCircleObjectPrivateQSG(QGeoMapObject *q) - : QMapCircleObjectPrivateDefault(q) + : QMapCircleObjectPrivateDefault(q), m_dataCPU(new CircleDataCPU) { } QMapCircleObjectPrivateQSG::QMapCircleObjectPrivateQSG(const QMapCircleObjectPrivate &other) - : QMapCircleObjectPrivateDefault(other) + : QMapCircleObjectPrivateDefault(other), m_dataCPU(new CircleDataCPU) { // Data already cloned by the *Default copy constructor, but necessary // update operations triggered only by setters overrides + if (!QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(center(), radius())) + switchToGL(); // this marks source dirty + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -62,47 +65,54 @@ QMapCircleObjectPrivateQSG::~QMapCircleObjectPrivateQSG() m_map->removeMapObject(q); } -void QMapCircleObjectPrivateQSG::updateCirclePath() +void QMapCircleObjectPrivateQSG::updateGeometry() +{ + if (!m_dataGL.isNull()) + updateGeometryGL(); + else + updateGeometryCPU(); +} + +void QMapCircleObjectPrivateQSG::CircleDataCPU::updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p) { - const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); QList path; - QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, center(), radius(), CircleSamples, m_leftBound); + QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, center, radius, CircleSamples, m_leftBound); m_circlePath.clear(); for (const QGeoCoordinate &c : path) m_circlePath << p.geoToMapProjection(c); } -void QMapCircleObjectPrivateQSG::updateGeometry() +void QMapCircleObjectPrivateQSG::updateGeometryCPU() { if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator || !qIsFinite(radius()) || !center().isValid()) return; const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); - QScopedValueRollback rollback(m_updatingGeometry); - m_updatingGeometry = true; + QScopedValueRollback rollback(m_dataCPU->m_updatingGeometry); + m_dataCPU->m_updatingGeometry = true; - updateCirclePath(); - QList circlePath = m_circlePath; + m_dataCPU->updateCirclePath(center(), radius(), p); + QList circlePath = m_dataCPU->m_circlePath; int pathCount = circlePath.size(); bool preserve = QDeclarativeCircleMapItemPrivateCPU::preserveCircleGeometry(circlePath, center(), radius(), p); // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft()); // to fix QTBUG-62154 - m_geometry.markSourceDirty(); - m_geometry.setPreserveGeometry(true, m_leftBound); // to set the geoLeftBound_ - m_geometry.setPreserveGeometry(preserve, m_leftBound); + m_dataCPU->m_geometry.markSourceDirty(); + m_dataCPU->m_geometry.setPreserveGeometry(true, m_dataCPU->m_leftBound); // to set the geoLeftBound_ + m_dataCPU->m_geometry.setPreserveGeometry(preserve, m_dataCPU->m_leftBound); bool invertedCircle = false; if (QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) { - m_geometry.updateScreenPointsInvert(circlePath, *m_map); // invert fill area for really huge circles + m_dataCPU->m_geometry.updateScreenPointsInvert(circlePath, *m_map); // invert fill area for really huge circles invertedCircle = true; } else { - m_geometry.updateSourcePoints(*m_map, circlePath); - m_geometry.updateScreenPoints(*m_map); + m_dataCPU->m_geometry.updateSourcePoints(*m_map, circlePath); + m_dataCPU->m_geometry.updateScreenPoints(*m_map); } - m_borderGeometry.clear(); + m_dataCPU->m_borderGeometry.clear(); //if (borderColor() != Qt::transparent && borderWidth() > 0) { @@ -110,35 +120,76 @@ void QMapCircleObjectPrivateQSG::updateGeometry() closedPath << closedPath.first(); if (invertedCircle) { - closedPath = m_circlePath; + closedPath = m_dataCPU->m_circlePath; closedPath << closedPath.first(); std::reverse(closedPath.begin(), closedPath.end()); } - m_borderGeometry.markSourceDirty(); - m_borderGeometry.setPreserveGeometry(true, m_leftBound); - m_borderGeometry.setPreserveGeometry(preserve, m_leftBound); + m_dataCPU->m_borderGeometry.markSourceDirty(); + m_dataCPU->m_borderGeometry.setPreserveGeometry(true, m_dataCPU->m_leftBound); + m_dataCPU->m_borderGeometry.setPreserveGeometry(preserve, m_dataCPU->m_leftBound); // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail. - const QGeoCoordinate &geometryOrigin = m_geometry.origin(); + const QGeoCoordinate &geometryOrigin = m_dataCPU->m_geometry.origin(); - m_borderGeometry.clearSource(); + m_dataCPU->m_borderGeometry.clearSource(); QDoubleVector2D borderLeftBoundWrapped; QList > clippedPaths = - m_borderGeometry.clipPath(*m_map, closedPath, borderLeftBoundWrapped); + m_dataCPU->m_borderGeometry.clipPath(*m_map, closedPath, borderLeftBoundWrapped); if (clippedPaths.size()) { borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); - m_borderGeometry.pathToScreen(*m_map, clippedPaths, borderLeftBoundWrapped); - m_borderGeometry.updateScreenPoints(*m_map, borderWidth(), false); + m_dataCPU->m_borderGeometry.pathToScreen(*m_map, clippedPaths, borderLeftBoundWrapped); + m_dataCPU->m_borderGeometry.updateScreenPoints(*m_map, borderWidth(), false); } else { - m_borderGeometry.clear(); + m_dataCPU->m_borderGeometry.clear(); } } - QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); - m_geometry.translate(origin - m_geometry.firstPointOffset()); - m_borderGeometry.translate(origin - m_borderGeometry.firstPointOffset()); + QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_dataCPU->m_geometry.origin(), false).toPointF(); + m_dataCPU->m_geometry.translate(origin - m_dataCPU->m_geometry.firstPointOffset()); + m_dataCPU->m_borderGeometry.translate(origin - m_dataCPU->m_borderGeometry.firstPointOffset()); +} + +void QMapCircleObjectPrivateQSG::CircleDataGL::updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p) +{ + m_circlePath.clear(); + if (radius < 0.001) // 1mm is small enough, probably already way too small. + return; + QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(m_circlePath, + center, + radius, + CircleSamples, + m_leftBound); + + m_leftBoundMercator = p.geoToMapProjection(m_leftBound); + m_geometry.setPreserveGeometry(true, m_leftBound); + m_borderGeometry.setPreserveGeometry(true, m_leftBound); +} + +void QMapCircleObjectPrivateQSG::updateGeometryGL() +{ + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + + const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); + if (m_dataGL->m_geometry.isSourceDirty() + || m_dataGL->m_borderGeometry.isSourceDirty()) { + m_dataGL->updateCirclePath(center(), radius(), p); + + if (m_dataGL->m_circlePath.length() == 0) { // Possibly cleared + m_dataGL->m_geometry.clear(); + m_dataGL->m_borderGeometry.clear(); + return; + } + m_dataGL->m_geometry.m_dataChanged = m_dataGL->m_borderGeometry.m_dataChanged = true; + m_dataGL->m_geometry.updateSourcePoints(*m_map, m_dataGL->m_circlePath); + m_dataGL->m_borderGeometry.updateSourcePoints(*m_map, QGeoCircle(center(), radius())); + m_dataGL->m_circlePath.clear(); // not needed anymore + } + m_dataGL->m_geometry.markScreenDirty(); // ToDo: this needs refactor. It's useless, remove screenDirty_ altogether. + m_dataGL->m_borderGeometry.markScreenDirty(); + m_dataGL->m_borderGeometry.m_wrapOffset = m_dataGL->m_geometry.m_wrapOffset = p.projectionWrapFactor(m_dataGL->m_leftBoundMercator) + 1; } QGeoMapObjectPrivate *QMapCircleObjectPrivateQSG::clone() @@ -146,45 +197,144 @@ QGeoMapObjectPrivate *QMapCircleObjectPrivateQSG::clone() return new QMapCircleObjectPrivateQSG(static_cast(*this)); } +void QMapCircleObjectPrivateQSG::switchToGL() +{ + if (!m_dataGL.isNull()) + return; + QScopedPointer data(new CircleDataGL); + m_dataGL.swap(data); + m_dataGL->markSourceDirty(); + m_dataCPU.reset(nullptr); +} + +void QMapCircleObjectPrivateQSG::switchToCPU() +{ + if (!m_dataCPU.isNull()) + return; + QScopedPointer data(new CircleDataCPU); + m_dataCPU.swap(data); + m_dataGL.reset(nullptr); +} + QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, QSGNode *root, - QQuickWindow * /*window*/) + QQuickWindow * window) { -// Q_UNUSED(visibleNode); // coz of -Werror=unused-but-set-parameter - MapPolygonNode *node = static_cast(oldNode); + if (!m_dataGL.isNull()) + return updateMapObjectNodeGL(oldNode, visibleNode, root, window); + else + return updateMapObjectNodeCPU(oldNode, visibleNode, root, window); +} - if (!node) { - if (!m_geometry.size() && !m_borderGeometry.size()) { - return nullptr; - } - node = new MapPolygonNode(); - *visibleNode = static_cast(node); +QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNodeCPU(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow */*window*/) +{ + if (!m_dataCPU->m_node || !oldNode) { + m_dataCPU->m_node = new MapPolygonNode(); + *visibleNode = static_cast(m_dataCPU->m_node); + if (oldNode) + delete oldNode; + } else { + m_dataCPU->m_node = static_cast(oldNode); + } + + if (!m_dataCPU->m_geometry.size() && !m_dataCPU->m_borderGeometry.size()) { + visibleNode = nullptr; + return nullptr; } //TODO: update only material - if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode) { + if (m_dataCPU->m_geometry.isScreenDirty() || m_dataCPU->m_borderGeometry.isScreenDirty() || oldNode != m_dataCPU->m_node) { //QMapPolygonObject *p = static_cast(q); - node->update(color(), borderColor(), &m_geometry, &m_borderGeometry); - m_geometry.setPreserveGeometry(false); - m_borderGeometry.setPreserveGeometry(false); - m_geometry.markClean(); - m_borderGeometry.markClean(); + m_dataCPU->m_node->update(color(), borderColor(), &m_dataCPU->m_geometry, &m_dataCPU->m_borderGeometry); + m_dataCPU->m_geometry.setPreserveGeometry(false); + m_dataCPU->m_borderGeometry.setPreserveGeometry(false); + m_dataCPU->m_geometry.markClean(); + m_dataCPU->m_borderGeometry.markClean(); } - if (m_geometry.size() || m_borderGeometry.size()) { - root->appendChildNode(node); + if (m_dataCPU->m_geometry.size() || m_dataCPU->m_borderGeometry.size()) { + root->appendChildNode(m_dataCPU->m_node); } else { - delete node; + delete m_dataCPU->m_node; + m_dataCPU->m_node = nullptr; + visibleNode = nullptr; return nullptr; } - return node; + return m_dataCPU->m_node; } +QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNodeGL(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow */*window*/) +{ + if (!m_dataGL->m_rootNode || !oldNode) { + m_dataGL->m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); + m_dataGL->m_node = new MapPolygonNodeGL(); + m_dataGL->m_rootNode->appendChildNode(m_dataGL->m_node); + m_dataGL->m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + m_dataGL->m_rootNode->appendChildNode(m_dataGL->m_polylinenode); + m_dataGL->m_rootNode->markDirty(QSGNode::DirtyNodeAdded); + *visibleNode = static_cast(m_dataGL->m_rootNode); + if (oldNode) + delete oldNode; + } else { + m_dataGL->m_rootNode = static_cast(oldNode); + } + + const QMatrix4x4 &combinedMatrix = m_map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = m_map->geoProjection().centerMercator(); + + if (m_dataGL->m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_dataGL->m_polylinenode->update(borderColor(), + float(borderWidth()), + &m_dataGL->m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true); + m_dataGL->m_borderGeometry.setPreserveGeometry(false); + m_dataGL->m_borderGeometry.markClean(); + } + if (m_dataGL->m_geometry.isScreenDirty()) { + m_dataGL->m_node->update(color(), + &m_dataGL->m_geometry, + combinedMatrix, + cameraCenter); + m_dataGL->m_geometry.setPreserveGeometry(false); + m_dataGL->m_geometry.markClean(); + } + + if (!m_dataGL->m_polylinenode->isSubtreeBlocked() || !m_dataGL->m_node->isSubtreeBlocked()) { + m_dataGL->m_rootNode->setSubtreeBlocked(false); + root->appendChildNode(m_dataGL->m_rootNode); + return m_dataGL->m_rootNode; + } else { + delete m_dataGL->m_rootNode; + m_dataGL->m_rootNode = nullptr; + m_dataGL->m_node = nullptr; + m_dataGL->m_polylinenode = nullptr; + *visibleNode = nullptr; + return nullptr; + } +} void QMapCircleObjectPrivateQSG::setCenter(const QGeoCoordinate ¢er) { QMapCircleObjectPrivateDefault::setCenter(center); + if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(this->center(), this->radius())) // Switching implementation for circles crossing/not crossing poles + switchToGL(); + else + switchToCPU(); + + if (!m_dataGL.isNull()) + m_dataGL->markSourceDirty(); + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -193,6 +343,14 @@ void QMapCircleObjectPrivateQSG::setCenter(const QGeoCoordinate ¢er) void QMapCircleObjectPrivateQSG::setRadius(qreal radius) { QMapCircleObjectPrivateDefault::setRadius(radius); + if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(this->center(), this->radius())) // Switching implementation for circles crossing/not crossing poles + switchToGL(); + else + switchToCPU(); + + if (!m_dataGL.isNull()) + m_dataGL->markSourceDirty(); + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -201,7 +359,8 @@ void QMapCircleObjectPrivateQSG::setRadius(qreal radius) void QMapCircleObjectPrivateQSG::setColor(const QColor &color) { QMapCircleObjectPrivateDefault::setColor(color); - updateGeometry(); + if (!m_dataCPU.isNull()) + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); } @@ -209,7 +368,8 @@ void QMapCircleObjectPrivateQSG::setColor(const QColor &color) void QMapCircleObjectPrivateQSG::setBorderColor(const QColor &color) { QMapCircleObjectPrivateDefault::setBorderColor(color); - updateGeometry(); + if (!m_dataCPU.isNull()) + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); } @@ -217,10 +377,10 @@ void QMapCircleObjectPrivateQSG::setBorderColor(const QColor &color) void QMapCircleObjectPrivateQSG::setBorderWidth(qreal width) { QMapCircleObjectPrivateDefault::setBorderWidth(width); - updateGeometry(); + if (!m_dataCPU.isNull()) + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); } - QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h index 6bbeb397..dc057c0b 100644 --- a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h @@ -68,14 +68,22 @@ public: QMapCircleObjectPrivateQSG(const QMapCircleObjectPrivate &other); ~QMapCircleObjectPrivateQSG() override; - void updateCirclePath(); - // QQSGMapObject void updateGeometry() override; + void updateGeometryCPU(); + void updateGeometryGL(); QSGNode *updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, QSGNode *root, QQuickWindow *window) override; + QSGNode *updateMapObjectNodeCPU(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window); + QSGNode *updateMapObjectNodeGL(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window); // QGeoMapCirclePrivate interface void setCenter(const QGeoCoordinate ¢er) override; @@ -87,15 +95,43 @@ public: // QGeoMapObjectPrivate QGeoMapObjectPrivate *clone() override; + void switchToGL(); + void switchToCPU(); + public: // Data Members +struct CircleDataCPU { + MapPolygonNode *m_node = nullptr; QList m_circlePath; QGeoCoordinate m_leftBound; QGeoMapCircleGeometry m_geometry; QGeoMapPolylineGeometry m_borderGeometry; bool m_updatingGeometry = false; + + void updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p); +}; +struct CircleDataGL { + QList m_circlePath; + QGeoCoordinate m_leftBound; + QDoubleVector2D m_leftBoundMercator; + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; + + void updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p); + void markSourceDirty() + { + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + } +}; + QScopedPointer m_dataCPU; + QScopedPointer m_dataGL; }; QT_END_NAMESPACE #endif // QMAPCIRCLEOBJECT_P_P_H + diff --git a/src/location/labs/qsg/qmappolygonobjectqsg.cpp b/src/location/labs/qsg/qmappolygonobjectqsg.cpp index 25473478..a8a1cb4b 100644 --- a/src/location/labs/qsg/qmappolygonobjectqsg.cpp +++ b/src/location/labs/qsg/qmappolygonobjectqsg.cpp @@ -52,6 +52,7 @@ QMapPolygonObjectPrivateQSG::QMapPolygonObjectPrivateQSG(const QMapPolygonObject { // Data already cloned by the *Default copy constructor, but necessary // update operations triggered only by setters overrides + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -63,67 +64,22 @@ QMapPolygonObjectPrivateQSG::~QMapPolygonObjectPrivateQSG() m_map->removeMapObject(q); } -QList QMapPolygonObjectPrivateQSG::projectPath() +void QMapPolygonObjectPrivateQSG::setPath(const QList &p) { - QList geopathProjected_; - if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) - return geopathProjected_; - - const QGeoProjectionWebMercator &p = - static_cast(m_map->geoProjection()); - geopathProjected_.reserve(m_path.path().size()); - for (const QGeoCoordinate &c : m_path.path()) - geopathProjected_ << p.geoToMapProjection(c); - return geopathProjected_; -} - -QSGNode *QMapPolygonObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, - VisibleNode **visibleNode, - QSGNode *root, - QQuickWindow * /*window*/) -{ - Q_UNUSED(visibleNode); - MapPolygonNode *node = static_cast(oldNode); - - if (!node) { - if (!m_geometry.size() && !m_borderGeometry.size()) { - return nullptr; - } - node = new MapPolygonNode(); - *visibleNode = static_cast(node); - } - - //TODO: update only material - if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode) { - node->update(fillColor(), borderColor(), &m_geometry, &m_borderGeometry); - m_geometry.setPreserveGeometry(false); - m_borderGeometry.setPreserveGeometry(false); - m_geometry.markClean(); - m_borderGeometry.markClean(); - } - - if (m_geometry.size() || m_borderGeometry.size()) { - root->appendChildNode(node); - } else { - delete node; - return nullptr; - } - return node; -} - -void QMapPolygonObjectPrivateQSG::setPath(const QList &path) -{ - QMapPolygonObjectPrivateDefault::setPath(path); + if (p == path()) + return; + QMapPolygonObjectPrivateDefault::setPath(p); + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); + emit static_cast(q)->pathChanged(); } void QMapPolygonObjectPrivateQSG::setFillColor(const QColor &color) { QMapPolygonObjectPrivateDefault::setFillColor(color); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -132,7 +88,6 @@ void QMapPolygonObjectPrivateQSG::setFillColor(const QColor &color) void QMapPolygonObjectPrivateQSG::setBorderColor(const QColor &color) { QMapPolygonObjectPrivateDefault::setBorderColor(color); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -141,7 +96,6 @@ void QMapPolygonObjectPrivateQSG::setBorderColor(const QColor &color) void QMapPolygonObjectPrivateQSG::setBorderWidth(qreal width) { QMapPolygonObjectPrivateDefault::setBorderWidth(width); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -158,59 +112,111 @@ void QMapPolygonObjectPrivateQSG::setGeoShape(const QGeoShape &shape) return; m_path = QGeoPathEager(shape); + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); emit static_cast(q)->pathChanged(); } +// This is called both when data changes and when viewport changes. +// so handle both cases (sourceDirty, !sourceDirty) void QMapPolygonObjectPrivateQSG::updateGeometry() { - if (!m_map || m_path.path().length() == 0 - || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - QScopedValueRollback rollback(m_updatingGeometry); - m_updatingGeometry = true; - - const QList &geopathProjected = projectPath(); - - m_geometry.markSourceDirty(); - m_geometry.setPreserveGeometry(true, m_path.boundingGeoRectangle().topLeft()); - m_geometry.updateSourcePoints(*m_map, geopathProjected); - m_geometry.updateScreenPoints(*m_map); - - m_borderGeometry.clear(); - - //if (border_.color() != Qt::transparent && border_.width() > 0) - { - const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); - QList closedPath = geopathProjected; - closedPath << closedPath.first(); - - m_borderGeometry.markSourceDirty(); + if (m_path.path().length() == 0) { // Possibly cleared + m_geometry.clear(); + m_borderGeometry.clear(); + return; + } + const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); + if (m_geometry.isSourceDirty() || m_borderGeometry.isSourceDirty()) { + // This works a bit differently than MapPolygon: + // the "screen bounds" aren't needed, so update only sources, + // regardless of the color, as color changes won't trigger polish(), + // and remember to flag m_dataChanged, that is in principle the same as + // sourceDirty_, but in practice is cleared in two different codepaths. + // sourceDirty_ is cleared in any case, dataChanged only if the primitive + // is effectively visible (e.g., not transparent or border not null) + m_geometry.setPreserveGeometry(true, m_path.boundingGeoRectangle().topLeft()); m_borderGeometry.setPreserveGeometry(true, m_path.boundingGeoRectangle().topLeft()); + m_geometry.m_dataChanged = m_borderGeometry.m_dataChanged = true; + m_geometry.updateSourcePoints(*m_map, m_path); + m_borderGeometry.updateSourcePoints(*m_map, m_path); + m_leftBoundMercator = p.geoToMapProjection(m_geometry.origin()); + } + m_geometry.markScreenDirty(); // ToDo: this needs refactor. It's useless, remove screenDirty_ altogether. + m_borderGeometry.markScreenDirty(); + m_borderGeometry.m_wrapOffset = m_geometry.m_wrapOffset = p.projectionWrapFactor(m_leftBoundMercator) + 1; +} - const QGeoCoordinate &geometryOrigin = m_geometry.origin(); - - m_borderGeometry.clearSource(); +QSGNode *QMapPolygonObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow * /*window*/) +{ + if (!m_rootNode || !oldNode) { + m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); + m_node = new MapPolygonNodeGL(); + m_rootNode->appendChildNode(m_node); + m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + m_rootNode->appendChildNode(m_polylinenode); + m_rootNode->markDirty(QSGNode::DirtyNodeAdded); + *visibleNode = static_cast(m_rootNode); + if (oldNode) + delete oldNode; + } else { + m_rootNode = static_cast(oldNode); + } - QDoubleVector2D borderLeftBoundWrapped; - QList > clippedPaths = - m_borderGeometry.clipPath(*m_map.data(), closedPath, borderLeftBoundWrapped); + const QMatrix4x4 &combinedMatrix = m_map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = m_map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(borderColor(), + float(borderWidth()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true); + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } + if (m_geometry.isScreenDirty()) { + m_node->update(fillColor(), + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + } - if (clippedPaths.size()) { - borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); - m_borderGeometry.pathToScreen(*m_map.data(), clippedPaths, borderLeftBoundWrapped); - m_borderGeometry.updateScreenPoints(*m_map.data(), borderWidth(), false); - } else { - m_borderGeometry.clear(); - } + if (!m_polylinenode->isSubtreeBlocked() || !m_node->isSubtreeBlocked()) { + m_rootNode->setSubtreeBlocked(false); + root->appendChildNode(m_rootNode); + return m_rootNode; + } else { + m_rootNode->setSubtreeBlocked(true); + // If the object is currently invisible, but not gone, + // it is reasonable to assume it will become visible again. + // However, better not to retain unused data. + delete m_rootNode; + m_rootNode = nullptr; + m_node = nullptr; + m_polylinenode = nullptr; + *visibleNode = nullptr; + return nullptr; } +} - QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); - m_geometry.translate(origin - m_geometry.firstPointOffset()); - m_borderGeometry.translate(origin - m_borderGeometry.firstPointOffset()); +void QMapPolygonObjectPrivateQSG::markSourceDirty() +{ + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); } QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h index 8e6ae8a8..9dcece74 100644 --- a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h @@ -67,6 +67,7 @@ public: QList projectPath(); // QQSGMapObject + void markSourceDirty(); void updateGeometry() override; QSGNode *updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, @@ -84,10 +85,12 @@ public: virtual void setGeoShape(const QGeoShape &shape) override; // Data Members - QGeoMapPolygonGeometry m_geometry; - QGeoMapPolylineGeometry m_borderGeometry; - - bool m_updatingGeometry = false; + QDoubleVector2D m_leftBoundMercator; + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; }; QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmappolylineobjectqsg.cpp b/src/location/labs/qsg/qmappolylineobjectqsg.cpp index 96f66565..817d4df5 100644 --- a/src/location/labs/qsg/qmappolylineobjectqsg.cpp +++ b/src/location/labs/qsg/qmappolylineobjectqsg.cpp @@ -53,9 +53,9 @@ QMapPolylineObjectPrivateQSG::QMapPolylineObjectPrivateQSG(QGeoMapObject *q) QMapPolylineObjectPrivateQSG::QMapPolylineObjectPrivateQSG(const QMapPolylineObjectPrivate &other) : QMapPolylineObjectPrivateDefault(other) { - m_geoPath.setPath(m_path); // rest of the data already cloned by the *Default copy constructor, but necessary // update operations triggered only by setters overrides + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -75,28 +75,31 @@ QList QMapPolylineObjectPrivateQSG::projectPath() const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); - geopathProjected_.reserve(m_geoPath.path().size()); - for (const QGeoCoordinate &c : m_geoPath.path()) + geopathProjected_.reserve(m_path.path().size()); + for (const QGeoCoordinate &c : m_path.path()) geopathProjected_ << p.geoToMapProjection(c); return geopathProjected_; } void QMapPolylineObjectPrivateQSG::updateGeometry() { - if (!m_map || m_geoPath.path().length() == 0 - || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - QScopedValueRollback rollback(m_updatingGeometry); - m_updatingGeometry = true; - m_geometry.markSourceDirty(); - const QList &geopathProjected = projectPath(); - m_geometry.setPreserveGeometry(true, m_geoPath.boundingGeoRectangle().topLeft()); - m_geometry.updateSourcePoints(*m_map.data(), geopathProjected, m_geoPath.boundingGeoRectangle().topLeft()); - m_geometry.updateScreenPoints(*m_map.data(), width(), false); + if (m_path.path().length() == 0) { // Possibly cleared + m_borderGeometry.clear(); + return; + } - QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); - m_geometry.translate(origin - m_geometry.firstPointOffset()); + const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); + if (m_borderGeometry.isSourceDirty()) { + m_borderGeometry.setPreserveGeometry(true, m_path.boundingGeoRectangle().topLeft()); + m_borderGeometry.m_dataChanged = true; + m_borderGeometry.updateSourcePoints(*m_map, m_path); + m_leftBoundMercator = p.geoToMapProjection(m_borderGeometry.origin()); + } + m_borderGeometry.markScreenDirty(); + m_borderGeometry.m_wrapOffset = p.projectionWrapFactor(m_leftBoundMercator) + 1; } QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, @@ -104,41 +107,52 @@ QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, QSGNode *root, QQuickWindow * /*window*/) { - Q_UNUSED(visibleNode); - MapPolylineNode *node = static_cast(oldNode); - - if (!node) { - if (!m_geometry.size()) // condition to block the subtree - return nullptr; - node = new MapPolylineNode(); - *visibleNode = static_cast(node); + if (!m_polylinenode || !oldNode) { + m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + *visibleNode = static_cast(m_polylinenode); + if (oldNode) + delete oldNode; + } else { + m_polylinenode = static_cast(oldNode); } - //TODO: update only material - if (m_geometry.isScreenDirty() || !oldNode) { - node->update(color(), &m_geometry); - m_geometry.setPreserveGeometry(false); - m_geometry.markClean(); + const QMatrix4x4 &combinedMatrix = m_map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = m_map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(color(), + float(width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true); + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); } - if (m_geometry.size()) { - root->appendChildNode(node); + if (!m_polylinenode->isSubtreeBlocked() ) { + m_polylinenode->setSubtreeBlocked(false); + root->appendChildNode(m_polylinenode); + return m_polylinenode; } else { - delete node; + delete m_polylinenode; + m_polylinenode = nullptr; + *visibleNode = nullptr; return nullptr; } - return node; } QList QMapPolylineObjectPrivateQSG::path() const { - return m_geoPath.path(); + return m_path.path(); } void QMapPolylineObjectPrivateQSG::setPath(const QList &path) { - m_path = path; - m_geoPath.setPath(path); + m_path.setPath(path); + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -147,7 +161,6 @@ void QMapPolylineObjectPrivateQSG::setPath(const QList &path) void QMapPolylineObjectPrivateQSG::setColor(const QColor &color) { QMapPolylineObjectPrivateDefault::setColor(color); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -156,7 +169,6 @@ void QMapPolylineObjectPrivateQSG::setColor(const QColor &color) void QMapPolylineObjectPrivateQSG::setWidth(qreal width) { QMapPolylineObjectPrivateDefault::setWidth(width); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -169,7 +181,12 @@ QGeoMapObjectPrivate *QMapPolylineObjectPrivateQSG::clone() QGeoShape QMapPolylineObjectPrivateQSG::geoShape() const { - return m_geoPath; + return m_path; +} + +void QMapPolylineObjectPrivateQSG::markSourceDirty() +{ + m_borderGeometry.markSourceDirty(); } QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h index e8eb5839..63ebcde9 100644 --- a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h @@ -68,6 +68,7 @@ public: QList projectPath(); // QQSGMapObject + void markSourceDirty(); void updateGeometry() override; QSGNode *updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, @@ -85,9 +86,9 @@ public: virtual QGeoShape geoShape() const override; // Data Members - QGeoPathEager m_geoPath; - QGeoMapPolylineGeometry m_geometry; - bool m_updatingGeometry = false; + QDoubleVector2D m_leftBoundMercator; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; }; QT_END_NAMESPACE -- cgit v1.2.1 From be7cbed7411d024d178377bd327d5916c80e02a0 Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Mon, 30 Dec 2019 13:27:29 +0100 Subject: Add geometry simplification to MapPolyline/MapPolylineObjectQSG This change introduces a metric-based implementation of the Ramer-Douglas-Peucker line simplification algorithm to generate a LOD pyramid for the polyline geometries. This comes with a related property (in MapItemBase), lodThreshold, that can be used to change the threshold after which no simplification will be used. By default the value of this property is 0, meaning that the behavior will be unchanged and no LOD will be used. This change also introduces LOD on map polyine objects QSG, for which no property is introduced, and there's a default threshold set to zoom level 12 (which appear to produce acceptable results). Finally, this patch makes use of a threadpool with 1 thread to enqueue geometry simplification tasks, which would otherwise freeze the UI when computing for the first time. Support for geometry simplification is currently added only to polylines. It might be of interest extending it to polygons as well, once a proper strategy for handling the simplification of inner holes has been identified. Finally, extending it to circles could be of interest, while potentially bringing only minor benefits, as circle geometries are currently fixed to 128 vertices. Also adds a MapObject-based delegate to the geojson viewer example. Task-number: QTBUG-46652 Task-number: QTBUG-38459 Task-number: QTBUG-49303 Change-Id: I64b5db4577962db17e5388812909285c9356ef0d Reviewed-by: Fabian Kosmale --- src/location/labs/qsg/qmapcircleobjectqsg.cpp | 2 +- src/location/labs/qsg/qmappolylineobjectqsg.cpp | 15 ++++++++++++++- src/location/labs/qsg/qmappolylineobjectqsg_p_p.h | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src/location/labs') diff --git a/src/location/labs/qsg/qmapcircleobjectqsg.cpp b/src/location/labs/qsg/qmapcircleobjectqsg.cpp index 5b1f5361..750b20f2 100644 --- a/src/location/labs/qsg/qmapcircleobjectqsg.cpp +++ b/src/location/labs/qsg/qmapcircleobjectqsg.cpp @@ -297,7 +297,7 @@ QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNodeGL(QSGNode *oldNode, combinedMatrix, cameraCenter, Qt::SquareCap, - true); + true); // No LOD for circles ATM m_dataGL->m_borderGeometry.setPreserveGeometry(false); m_dataGL->m_borderGeometry.markClean(); } diff --git a/src/location/labs/qsg/qmappolylineobjectqsg.cpp b/src/location/labs/qsg/qmappolylineobjectqsg.cpp index 817d4df5..8efbfc2f 100644 --- a/src/location/labs/qsg/qmappolylineobjectqsg.cpp +++ b/src/location/labs/qsg/qmappolylineobjectqsg.cpp @@ -102,6 +102,18 @@ void QMapPolylineObjectPrivateQSG::updateGeometry() m_borderGeometry.m_wrapOffset = p.projectionWrapFactor(m_leftBoundMercator) + 1; } +/*! + \internal +*/ +unsigned int QMapPolylineObjectPrivateQSG::zoomForLOD(int zoom) const +{ + // LOD Threshold currently fixed to 12 for MapPolylineObject(QSG). + // ToDo: Consider allowing to change this via DynamicParameter. + if (zoom >= 12) + return 30; + return uint(zoom); +} + QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, QSGNode *root, @@ -127,7 +139,8 @@ QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, combinedMatrix, cameraCenter, Qt::SquareCap, - true); + true, + zoomForLOD(int(m_map->cameraData().zoomLevel()))); m_borderGeometry.setPreserveGeometry(false); m_borderGeometry.markClean(); } diff --git a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h index 63ebcde9..8bba2703 100644 --- a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h @@ -85,6 +85,8 @@ public: QGeoMapObjectPrivate *clone() override; virtual QGeoShape geoShape() const override; + unsigned int zoomForLOD(int zoom) const; + // Data Members QDoubleVector2D m_leftBoundMercator; QGeoMapPolylineGeometryOpenGL m_borderGeometry; -- cgit v1.2.1 From 0ace8d20dcdbcc281c7ebc48b0c153170ebd99b0 Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Wed, 12 Feb 2020 22:56:54 +0100 Subject: Fix crash with empty maps on construction Change-Id: I8716c94b2c3f1fb995e933fe5be736e94ff161ce Reviewed-by: Alex Blasche --- src/location/labs/qsg/qgeomapobjectqsgsupport.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/location/labs') diff --git a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp index f965efb7..0e1df8f6 100644 --- a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp +++ b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp @@ -155,6 +155,8 @@ void QGeoMapObjectQSGSupport::removeMapObject(QGeoMapObject *obj) // called in the render thread void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *window) { + if (!root) + return; if (!m_mapObjectsRootNode) { m_mapObjectsRootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); root->appendChildNode(m_mapObjectsRootNode); -- cgit v1.2.1 From a0e88ed10cd07d4bdf79d7e7f05f01179f2eb296 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Thu, 5 Mar 2020 16:42:33 +0100 Subject: Doc: Fix highlighing of import Change-Id: Ifc34a27c89d7d8a63e66e9858f8e6dd3cd9522ab Reviewed-by: Leena Miettinen --- src/location/labs/qdeclarativenavigator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/location/labs') diff --git a/src/location/labs/qdeclarativenavigator.cpp b/src/location/labs/qdeclarativenavigator.cpp index 69f450f3..546d0b01 100644 --- a/src/location/labs/qdeclarativenavigator.cpp +++ b/src/location/labs/qdeclarativenavigator.cpp @@ -52,9 +52,9 @@ QT_BEGIN_NAMESPACE To use this module, import the module with the following line: - \code + \qml import Qt.labs.location 1.0 - \endcode + \endqml \note These types are experimental and subject to source-incompatible changes from one Qt minor release to the next, until they are ready to be moved to the stable QtLocation QML -- cgit v1.2.1