diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2016-02-18 13:52:53 -0800 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2016-04-15 09:56:18 -0700 |
commit | ce4270b01c692bcb1af64a774e2d595438057375 (patch) | |
tree | 88fd6d1bee21058f366bc96e14d48364c163ef37 | |
parent | 6e78eabfff75579286ab23cbde8a36fba061ca1d (diff) | |
download | qtlocation-mapboxgl-ce4270b01c692bcb1af64a774e2d595438057375.tar.gz |
[core] Convert MapContext to Map::Impl; eliminate indirection
-rw-r--r-- | include/mbgl/map/map.hpp | 21 | ||||
-rw-r--r-- | src/mbgl/map/map.cpp | 488 | ||||
-rw-r--r-- | src/mbgl/map/map_context.cpp | 316 | ||||
-rw-r--r-- | src/mbgl/map/map_context.hpp | 116 | ||||
-rw-r--r-- | src/mbgl/renderer/painter.cpp | 1 | ||||
-rw-r--r-- | src/mbgl/renderer/painter.hpp | 10 | ||||
-rw-r--r-- | src/mbgl/renderer/painter_background.cpp | 1 | ||||
-rw-r--r-- | test/map/map.cpp | 13 | ||||
-rw-r--r-- | test/map/map_context.cpp | 26 | ||||
-rw-r--r-- | test/test.gypi | 2 |
10 files changed, 404 insertions, 590 deletions
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index b70c388183..e33b8e49f3 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -8,7 +8,6 @@ #include <mbgl/map/mode.hpp> #include <mbgl/util/geo.hpp> #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/vec.hpp> #include <mbgl/annotation/annotation.hpp> #include <mbgl/style/types.hpp> #include <mbgl/style/property_transition.hpp> @@ -23,18 +22,13 @@ namespace mbgl { class FileSource; class View; -class MapData; -class MapContext; class SpriteImage; -class Transform; class PointAnnotation; class ShapeAnnotation; struct CameraOptions; struct AnimationOptions; class Map : private util::noncopyable { - friend class View; - public: explicit Map(View&, FileSource&, MapMode mapMode = MapMode::Continuous, @@ -176,19 +170,8 @@ public: void dumpDebugLogs() const; private: - View& view; - const std::unique_ptr<Transform> transform; - const std::unique_ptr<MapContext> context; - MapData* data; - - enum class RenderState { - never, - partial, - fully - }; - - RenderState renderState = RenderState::never; - bool loading = false; + class Impl; + const std::unique_ptr<Impl> impl; }; } // namespace mbgl diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 7300e5c3bb..55f0197dcb 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -1,5 +1,4 @@ #include <mbgl/map/map.hpp> -#include <mbgl/map/map_context.hpp> #include <mbgl/map/camera.hpp> #include <mbgl/map/view.hpp> #include <mbgl/map/transform.hpp> @@ -7,57 +6,179 @@ #include <mbgl/map/map_data.hpp> #include <mbgl/annotation/point_annotation.hpp> #include <mbgl/annotation/shape_annotation.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/property_transition.hpp> #include <mbgl/layer/custom_layer.hpp> - +#include <mbgl/renderer/painter.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> +#include <mbgl/gl/gl_object_store.hpp> +#include <mbgl/gl/texture_pool.hpp> #include <mbgl/util/projection.hpp> #include <mbgl/util/math.hpp> +#include <mbgl/util/exception.hpp> +#include <mbgl/util/async_task.hpp> +#include <mbgl/util/mapbox.hpp> namespace mbgl { -Map::Map(View& view_, FileSource& fileSource, MapMode mapMode, GLContextMode contextMode, ConstrainMode constrainMode) - : view(view_), - transform(std::make_unique<Transform>(view, constrainMode)), - context(std::make_unique<MapContext>( - view, fileSource, mapMode, contextMode, view.getPixelRatio())), - data(&context->getData()) -{ +enum class RenderState { + never, + partial, + fully +}; + +class Map::Impl : public Style::Observer { +public: + Impl(View&, FileSource&, MapMode, GLContextMode, ConstrainMode); + + void onResourceLoaded() override; + void onResourceError(std::exception_ptr) override; + + void update(); + void render(); + + void loadStyleJSON(const std::string& json, const std::string& base); + + View& view; + FileSource& fileSource; + + RenderState renderState = RenderState::never; + Transform transform; + + std::unique_ptr<MapData> dataPtr; + MapData& data; + + gl::GLObjectStore glObjectStore; + Update updateFlags = Update::Nothing; + util::AsyncTask asyncUpdate; + + std::unique_ptr<gl::TexturePool> texturePool; + std::unique_ptr<Painter> painter; + std::unique_ptr<Style> style; + + std::string styleURL; + std::string styleJSON; + + std::unique_ptr<AsyncRequest> styleRequest; + + Map::StillImageCallback callback; + size_t sourceCacheSize; + TransformState transformState; + FrameData frameData; + bool loading = false; +}; + +Map::Map(View& view, FileSource& fileSource, MapMode mapMode, GLContextMode contextMode, ConstrainMode constrainMode) + : impl(std::make_unique<Impl>(view, fileSource, mapMode, contextMode, constrainMode)) { view.initialize(this); update(Update::Dimensions); } +Map::Impl::Impl(View& view_, FileSource& fileSource_, MapMode mode_, GLContextMode contextMode_, ConstrainMode constrainMode_) + : view(view_), + fileSource(fileSource_), + transform(view, constrainMode_), + dataPtr(std::make_unique<MapData>(mode_, contextMode_, view_.getPixelRatio())), + data(*dataPtr), + asyncUpdate([this] { update(); }), + texturePool(std::make_unique<gl::TexturePool>()) { +} + Map::~Map() { - context->cleanup(); + impl->view.activate(); + + impl->styleRequest = nullptr; + + // Explicit resets currently necessary because these abandon resources that need to be + // cleaned up by glObjectStore.performCleanup(); + impl->style.reset(); + impl->painter.reset(); + impl->texturePool.reset(); + impl->dataPtr.reset(); + + impl->glObjectStore.performCleanup(); + + impl->view.deactivate(); } void Map::renderStill(StillImageCallback callback) { - context->renderStill(transform->getState(), - FrameData { view.getFramebufferSize(), Clock::now() }, callback); + if (!callback) { + Log::Error(Event::General, "StillImageCallback not set"); + return; + } + + if (impl->data.mode != MapMode::Still) { + callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")), {}); + return; + } + + if (impl->callback) { + callback(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")), {}); + return; + } + + if (!impl->style) { + callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")), {}); + return; + } + + if (impl->style->getLastError()) { + callback(impl->style->getLastError(), {}); + return; + } + + impl->callback = callback; + impl->transformState = impl->transform.getState(); + impl->frameData = FrameData{ impl->view.getFramebufferSize(), Clock::now() }; + + impl->updateFlags |= Update::RenderStill; + impl->asyncUpdate.send(); +} + +void Map::update(Update flags) { + if (flags & Update::Dimensions) { + impl->transform.resize(impl->view.getSize()); + } + + impl->transformState = impl->transform.getState(); + impl->updateFlags |= flags; + + impl->asyncUpdate.send(); } void Map::render() { - if (renderState == RenderState::never) { - view.notifyMapChange(MapChangeWillStartRenderingMap); + if (!impl->style) { + return; } - view.notifyMapChange(MapChangeWillStartRenderingFrame); + if (impl->renderState == RenderState::never) { + impl->view.notifyMapChange(MapChangeWillStartRenderingMap); + } + + impl->view.notifyMapChange(MapChangeWillStartRenderingFrame); + + const Update flags = impl->transform.updateTransitions(Clock::now()); + + impl->transformState = impl->transform.getState(); + impl->frameData = FrameData { impl->view.getFramebufferSize(), Clock::now() }; - const Update flags = transform->updateTransitions(Clock::now()); - const bool fullyLoaded = context->renderSync(transform->getState(), FrameData { view.getFramebufferSize(), Clock::now() }); + impl->render(); - view.notifyMapChange(fullyLoaded ? + impl->view.notifyMapChange(isFullyLoaded() ? MapChangeDidFinishRenderingFrameFullyRendered : MapChangeDidFinishRenderingFrame); - if (!fullyLoaded) { - renderState = RenderState::partial; - } else if (renderState != RenderState::fully) { - renderState = RenderState::fully; - view.notifyMapChange(MapChangeDidFinishRenderingMapFullyRendered); - if (loading) { - loading = false; - view.notifyMapChange(MapChangeDidFinishLoadingMap); + if (!isFullyLoaded()) { + impl->renderState = RenderState::partial; + } else if (impl->renderState != RenderState::fully) { + impl->renderState = RenderState::fully; + impl->view.notifyMapChange(MapChangeDidFinishRenderingMapFullyRendered); + if (impl->loading) { + impl->loading = false; + impl->view.notifyMapChange(MapChangeDidFinishLoadingMap); } } @@ -68,83 +189,195 @@ void Map::render() { } } -void Map::update(Update flags) { - if (flags & Update::Dimensions) transform->resize(view.getSize()); - context->triggerUpdate(transform->getState(), flags); +void Map::Impl::update() { + if (!style) { + updateFlags = Update::Nothing; + } + + if (updateFlags == Update::Nothing || (data.mode == MapMode::Still && !callback)) { + return; + } + + // This time point is used to: + // - Calculate style property transitions; + // - Hint style sources to notify when all its tiles are loaded; + frameData.timePoint = Clock::now(); + + if (style->loaded && updateFlags & Update::Annotations) { + data.getAnnotationManager()->updateStyle(*style); + updateFlags |= Update::Classes; + } + + if (updateFlags & Update::Classes) { + style->cascade(frameData.timePoint); + } + + if (updateFlags & Update::Classes || updateFlags & Update::RecalculateStyle) { + style->recalculate(transformState.getZoom(), frameData.timePoint); + } + + style->update(transformState, frameData.timePoint, *texturePool); + + if (data.mode == MapMode::Continuous) { + view.invalidate(); + } else if (callback && style->isLoaded()) { + view.activate(); + render(); + view.deactivate(); + } + + updateFlags = Update::Nothing; +} + +void Map::Impl::render() { + if (!painter) { + painter = std::make_unique<Painter>(data, transformState, glObjectStore); + } + + painter->render(*style, + frameData, + data.getAnnotationManager()->getSpriteAtlas()); + + if (data.mode == MapMode::Still) { + callback(nullptr, view.readStillImage()); + callback = nullptr; + } + + glObjectStore.performCleanup(); + + if (style->hasTransitions()) { + updateFlags |= Update::RecalculateStyle; + asyncUpdate.send(); + } else if (painter->needsAnimation()) { + updateFlags |= Update::Repaint; + asyncUpdate.send(); + } } #pragma mark - Style -void Map::setStyleURL(const std::string &url) { - loading = true; - view.notifyMapChange(MapChangeWillStartLoadingMap); - context->setStyleURL(url); +void Map::setStyleURL(const std::string& url) { + if (impl->styleURL == url) { + return; + } + + impl->loading = true; + + impl->view.notifyMapChange(MapChangeWillStartLoadingMap); + + impl->styleRequest = nullptr; + impl->styleURL = url; + impl->styleJSON.clear(); + + impl->style = std::make_unique<Style>(impl->data, impl->fileSource); + + const size_t pos = impl->styleURL.rfind('/'); + std::string base = ""; + if (pos != std::string::npos) { + base = impl->styleURL.substr(0, pos + 1); + } + + impl->styleRequest = impl->fileSource.request(Resource::style(impl->styleURL), [this, base](Response res) { + if (res.error) { + if (res.error->reason == Response::Error::Reason::NotFound && + util::mapbox::isMapboxURL(impl->styleURL)) { + Log::Error(Event::Setup, "style %s could not be found or is an incompatible legacy map or style", impl->styleURL.c_str()); + } else { + Log::Error(Event::Setup, "loading style failed: %s", res.error->message.c_str()); + } + } else if (res.notModified || res.noContent) { + return; + } else { + impl->loadStyleJSON(*res.data, base); + } + }); } void Map::setStyleJSON(const std::string& json, const std::string& base) { - loading = true; - view.notifyMapChange(MapChangeWillStartLoadingMap); - context->setStyleJSON(json, base); + if (impl->styleJSON == json) { + return; + } + + impl->loading = true; + + impl->view.notifyMapChange(MapChangeWillStartLoadingMap); + + impl->styleURL.clear(); + impl->styleJSON.clear(); + impl->style = std::make_unique<Style>(impl->data, impl->fileSource); + + impl->loadStyleJSON(json, base); +} + +void Map::Impl::loadStyleJSON(const std::string& json, const std::string& base) { + style->setJSON(json, base); + style->setObserver(this); + styleJSON = json; + + // force style cascade, causing all pending transitions to complete. + style->cascade(Clock::now()); + + updateFlags |= Update::Classes | Update::RecalculateStyle | Update::Annotations; + asyncUpdate.send(); } std::string Map::getStyleURL() const { - return context->getStyleURL(); + return impl->styleURL; } std::string Map::getStyleJSON() const { - return context->getStyleJSON(); + return impl->styleJSON; } #pragma mark - Transitions void Map::cancelTransitions() { - transform->cancelTransitions(); + impl->transform.cancelTransitions(); update(Update::Repaint); } void Map::setGestureInProgress(bool inProgress) { - transform->setGestureInProgress(inProgress); + impl->transform.setGestureInProgress(inProgress); update(Update::Repaint); } bool Map::isGestureInProgress() const { - return transform->isGestureInProgress(); + return impl->transform.isGestureInProgress(); } bool Map::isRotating() const { - return transform->isRotating(); + return impl->transform.isRotating(); } bool Map::isScaling() const { - return transform->isScaling(); + return impl->transform.isScaling(); } bool Map::isPanning() const { - return transform->isPanning(); + return impl->transform.isPanning(); } #pragma mark - void Map::jumpTo(const CameraOptions& camera) { - transform->jumpTo(camera); + impl->transform.jumpTo(camera); update(camera.zoom ? Update::RecalculateStyle : Update::Repaint); } void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) { - transform->easeTo(camera, animation); + impl->transform.easeTo(camera, animation); update(camera.zoom ? Update::RecalculateStyle : Update::Repaint); } - - + void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) { - transform->flyTo(camera, animation); + impl->transform.flyTo(camera, animation); update(Update::RecalculateStyle); } #pragma mark - Position void Map::moveBy(const ScreenCoordinate& point, const Duration& duration) { - transform->moveBy(point, duration); + impl->transform.moveBy(point, duration); update(Update::Repaint); } @@ -153,17 +386,17 @@ void Map::setLatLng(const LatLng& latLng, const Duration& duration) { } void Map::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const Duration& duration) { - transform->setLatLng(latLng, padding, duration); + impl->transform.setLatLng(latLng, padding, duration); update(Update::Repaint); } void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const Duration& duration) { - transform->setLatLng(latLng, anchor, duration); + impl->transform.setLatLng(latLng, anchor, duration); update(Update::Repaint); } LatLng Map::getLatLng(optional<EdgeInsets> padding) const { - return transform->getLatLng(padding); + return impl->transform.getLatLng(padding); } void Map::resetPosition(optional<EdgeInsets> padding) { @@ -173,7 +406,7 @@ void Map::resetPosition(optional<EdgeInsets> padding) { camera.center = LatLng(0, 0); camera.padding = padding; camera.zoom = 0; - transform->jumpTo(camera); + impl->transform.jumpTo(camera); update(Update::RecalculateStyle); } @@ -181,17 +414,17 @@ void Map::resetPosition(optional<EdgeInsets> padding) { #pragma mark - Scale void Map::scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& duration) { - transform->scaleBy(ds, anchor, duration); + impl->transform.scaleBy(ds, anchor, duration); update(Update::RecalculateStyle); } void Map::setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& duration) { - transform->setScale(scale, anchor, duration); + impl->transform.setScale(scale, anchor, duration); update(Update::RecalculateStyle); } double Map::getScale() const { - return transform->getScale(); + return impl->transform.getScale(); } void Map::setZoom(double zoom, const Duration& duration) { @@ -199,12 +432,12 @@ void Map::setZoom(double zoom, const Duration& duration) { } void Map::setZoom(double zoom, optional<EdgeInsets> padding, const Duration& duration) { - transform->setZoom(zoom, padding, duration); + impl->transform.setZoom(zoom, padding, duration); update(Update::RecalculateStyle); } double Map::getZoom() const { - return transform->getZoom(); + return impl->transform.getZoom(); } void Map::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& duration) { @@ -212,7 +445,7 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& durat } void Map::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const Duration& duration) { - transform->setLatLngZoom(latLng, zoom, padding, duration); + impl->transform.setLatLngZoom(latLng, zoom, padding, duration); update(Update::RecalculateStyle); } @@ -285,43 +518,41 @@ void Map::resetZoom() { } void Map::setMinZoom(const double minZoom) { - transform->setMinZoom(minZoom); + impl->transform.setMinZoom(minZoom); if (getZoom() < minZoom) { setZoom(minZoom); } } double Map::getMinZoom() const { - return transform->getState().getMinZoom(); + return impl->transform.getState().getMinZoom(); } void Map::setMaxZoom(const double maxZoom) { - transform->setMaxZoom(maxZoom); + impl->transform.setMaxZoom(maxZoom); if (getZoom() > maxZoom) { setZoom(maxZoom); } } double Map::getMaxZoom() const { - return transform->getState().getMaxZoom(); + return impl->transform.getState().getMaxZoom(); } - #pragma mark - Size uint16_t Map::getWidth() const { - return transform->getState().getWidth(); + return impl->transform.getState().getWidth(); } uint16_t Map::getHeight() const { - return transform->getState().getHeight(); + return impl->transform.getState().getHeight(); } - #pragma mark - Rotation void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& duration) { - transform->rotateBy(first, second, duration); + impl->transform.rotateBy(first, second, duration); update(Update::Repaint); } @@ -330,25 +561,24 @@ void Map::setBearing(double degrees, const Duration& duration) { } void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const Duration& duration) { - transform->setAngle(-degrees * util::DEG2RAD, anchor, duration); + impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, duration); update(Update::Repaint); } void Map::setBearing(double degrees, optional<EdgeInsets> padding, const Duration& duration) { - transform->setAngle(-degrees * util::DEG2RAD, padding, duration); + impl->transform.setAngle(-degrees * util::DEG2RAD, padding, duration); update(Update::Repaint); } double Map::getBearing() const { - return -transform->getAngle() * util::RAD2DEG; + return -impl->transform.getAngle() * util::RAD2DEG; } void Map::resetNorth(const Duration& duration) { - transform->setAngle(0, duration); + impl->transform.setAngle(0, duration); update(Update::Repaint); } - #pragma mark - Pitch void Map::setPitch(double pitch, const Duration& duration) { @@ -356,35 +586,34 @@ void Map::setPitch(double pitch, const Duration& duration) { } void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& duration) { - transform->setPitch(pitch * util::DEG2RAD, anchor, duration); + impl->transform.setPitch(pitch * util::DEG2RAD, anchor, duration); update(Update::Repaint); } double Map::getPitch() const { - return transform->getPitch() * util::RAD2DEG; + return impl->transform.getPitch() * util::RAD2DEG; } - #pragma mark - North Orientation void Map::setNorthOrientation(NorthOrientation orientation) { - transform->setNorthOrientation(orientation); + impl->transform.setNorthOrientation(orientation); update(Update::Repaint); } NorthOrientation Map::getNorthOrientation() const { - return transform->getNorthOrientation(); + return impl->transform.getNorthOrientation(); } #pragma mark - Constrain mode void Map::setConstrainMode(mbgl::ConstrainMode mode) { - transform->setConstrainMode(mode); + impl->transform.setConstrainMode(mode); update(Update::Repaint); } ConstrainMode Map::getConstrainMode() const { - return transform->getConstrainMode(); + return impl->transform.getConstrainMode(); } #pragma mark - Projection @@ -402,25 +631,25 @@ LatLng Map::latLngForProjectedMeters(const ProjectedMeters& projectedMeters) con } ScreenCoordinate Map::pixelForLatLng(const LatLng& latLng) const { - return transform->latLngToScreenCoordinate(latLng); + return impl->transform.latLngToScreenCoordinate(latLng); } LatLng Map::latLngForPixel(const ScreenCoordinate& pixel) const { - return transform->screenCoordinateToLatLng(pixel); + return impl->transform.screenCoordinateToLatLng(pixel); } #pragma mark - Annotations void Map::addAnnotationIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) { - context->addAnnotationIcon(name, sprite); + impl->data.getAnnotationManager()->addIcon(name, sprite); } void Map::removeAnnotationIcon(const std::string& name) { - context->removeAnnotationIcon(name); + impl->data.getAnnotationManager()->removeIcon(name); } -double Map::getTopOffsetPixelsForAnnotationIcon(const std::string& symbol) { - return context->getTopOffsetPixelsForAnnotationIcon(symbol); +double Map::getTopOffsetPixelsForAnnotationIcon(const std::string& name) { + return impl->data.getAnnotationManager()->getTopOffsetPixelsForIcon(name); } AnnotationID Map::addPointAnnotation(const PointAnnotation& annotation) { @@ -428,7 +657,7 @@ AnnotationID Map::addPointAnnotation(const PointAnnotation& annotation) { } AnnotationIDs Map::addPointAnnotations(const std::vector<PointAnnotation>& annotations) { - auto result = data->getAnnotationManager()->addPointAnnotations(annotations, getMaxZoom()); + auto result = impl->data.getAnnotationManager()->addPointAnnotations(annotations, getMaxZoom()); update(Update::Annotations); return result; } @@ -438,13 +667,13 @@ AnnotationID Map::addShapeAnnotation(const ShapeAnnotation& annotation) { } AnnotationIDs Map::addShapeAnnotations(const std::vector<ShapeAnnotation>& annotations) { - auto result = data->getAnnotationManager()->addShapeAnnotations(annotations, getMaxZoom()); + auto result = impl->data.getAnnotationManager()->addShapeAnnotations(annotations, getMaxZoom()); update(Update::Annotations); return result; } void Map::updatePointAnnotation(AnnotationID annotationId, const PointAnnotation& annotation) { - data->getAnnotationManager()->updatePointAnnotation(annotationId, annotation, getMaxZoom()); + impl->data.getAnnotationManager()->updatePointAnnotation(annotationId, annotation, getMaxZoom()); update(Update::Annotations); } @@ -453,12 +682,12 @@ void Map::removeAnnotation(AnnotationID annotation) { } void Map::removeAnnotations(const AnnotationIDs& annotations) { - data->getAnnotationManager()->removeAnnotations(annotations); + impl->data.getAnnotationManager()->removeAnnotations(annotations); update(Update::Annotations); } AnnotationIDs Map::getPointAnnotationsInBounds(const LatLngBounds& bounds) { - return data->getAnnotationManager()->getPointAnnotationsInBounds(bounds); + return impl->data.getAnnotationManager()->getPointAnnotationsInBounds(bounds); } #pragma mark - Style API @@ -469,69 +698,108 @@ void Map::addCustomLayer(const std::string& id, CustomLayerDeinitializeFunction deinitialize, void* context_, const char* before) { - view.activate(); - context->addLayer( + impl->view.activate(); + + impl->style->addLayer( std::make_unique<CustomLayer>(id, initialize, render_, deinitialize, context_), before ? std::string(before) : optional<std::string>()); - view.deactivate(); + impl->updateFlags |= Update::Classes; + impl->asyncUpdate.send(); + + impl->view.deactivate(); } void Map::removeCustomLayer(const std::string& id) { - view.activate(); - context->removeLayer(id); - view.deactivate(); + impl->view.activate(); + + impl->style->removeLayer(id); + impl->updateFlags |= Update::Classes; + impl->asyncUpdate.send(); + + impl->view.deactivate(); } #pragma mark - Toggles void Map::setDebug(MapDebugOptions mode) { - data->setDebug(mode); + impl->data.setDebug(mode); update(Update::Repaint); } void Map::cycleDebugOptions() { - data->cycleDebugOptions(); + impl->data.cycleDebugOptions(); update(Update::Repaint); } MapDebugOptions Map::getDebug() const { - return data->getDebug(); + return impl->data.getDebug(); } bool Map::isFullyLoaded() const { - return context->isLoaded(); + return impl->style->isLoaded(); } void Map::addClass(const std::string& className, const PropertyTransition& properties) { - context->addClass(className, properties); + if (impl->style->addClass(className, properties)) { + update(Update::Classes); + } } void Map::removeClass(const std::string& className, const PropertyTransition& properties) { - context->removeClass(className, properties); + if (impl->style->removeClass(className, properties)) { + update(Update::Classes); + } } void Map::setClasses(const std::vector<std::string>& classNames, const PropertyTransition& properties) { - context->setClasses(classNames, properties); + impl->style->setClasses(classNames, properties); + update(Update::Classes); } bool Map::hasClass(const std::string& className) const { - return context->hasClass(className); + return impl->style->hasClass(className); } std::vector<std::string> Map::getClasses() const { - return context->getClasses(); + return impl->style->getClasses(); } void Map::setSourceTileCacheSize(size_t size) { - context->setSourceTileCacheSize(size); + if (size != impl->sourceCacheSize) { + impl->sourceCacheSize = size; + if (!impl->style) return; + impl->style->setSourceTileCacheSize(size); + impl->view.invalidate(); + } } void Map::onLowMemory() { - context->onLowMemory(); + if (!impl->style) return; + impl->style->onLowMemory(); + impl->view.invalidate(); +} + +void Map::Impl::onResourceLoaded() { + updateFlags |= Update::Repaint; + asyncUpdate.send(); +} + +void Map::Impl::onResourceError(std::exception_ptr error) { + if (data.mode == MapMode::Still && callback) { + callback(error, {}); + callback = nullptr; + } } void Map::dumpDebugLogs() const { - context->dumpDebugLogs(); + Log::Info(Event::General, "--------------------------------------------------------------------------------"); + Log::Info(Event::General, "MapContext::styleURL: %s", impl->styleURL.c_str()); + if (impl->style) { + impl->style->dumpDebugLogs(); + } else { + Log::Info(Event::General, "no style loaded"); + } + Log::Info(Event::General, "--------------------------------------------------------------------------------"); } } // namespace mbgl diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp deleted file mode 100644 index 5b6cd2af74..0000000000 --- a/src/mbgl/map/map_context.cpp +++ /dev/null @@ -1,316 +0,0 @@ -#include <mbgl/map/map_context.hpp> -#include <mbgl/map/map_data.hpp> -#include <mbgl/map/view.hpp> - -#include <mbgl/platform/log.hpp> - -#include <mbgl/renderer/painter.hpp> - -#include <mbgl/storage/file_source.hpp> -#include <mbgl/storage/resource.hpp> -#include <mbgl/storage/response.hpp> - -#include <mbgl/style/style.hpp> -#include <mbgl/style/style_layer.hpp> -#include <mbgl/style/property_transition.hpp> - -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/sprite/sprite_store.hpp> - -#include <mbgl/gl/gl_object_store.hpp> -#include <mbgl/gl/texture_pool.hpp> - -#include <mbgl/util/worker.hpp> -#include <mbgl/util/exception.hpp> -#include <mbgl/util/string.hpp> -#include <mbgl/util/mapbox.hpp> - -#include <algorithm> - -namespace mbgl { - -MapContext::MapContext(View& view_, FileSource& fileSource_, MapMode mode_, GLContextMode contextMode_, const float pixelRatio_) - : view(view_), - fileSource(fileSource_), - dataPtr(std::make_unique<MapData>(mode_, contextMode_, pixelRatio_)), - data(*dataPtr), - asyncUpdate([this] { update(); }), - texturePool(std::make_unique<gl::TexturePool>()) { -} - -MapContext::~MapContext() { - // Make sure we call cleanup() before deleting this object. - assert(!style); -} - -void MapContext::cleanup() { - view.activate(); - - styleRequest = nullptr; - - // Explicit resets currently necessary because these abandon resources that need to be - // cleaned up by glObjectStore.performCleanup(); - style.reset(); - painter.reset(); - texturePool.reset(); - dataPtr.reset(); - - glObjectStore.performCleanup(); - - view.deactivate(); -} - -void MapContext::updateAsync(Update flags) { - updateFlags |= flags; - asyncUpdate.send(); -} - -void MapContext::triggerUpdate(const TransformState& state, Update flags) { - transformState = state; - updateAsync(flags); -} - -void MapContext::setStyleURL(const std::string& url) { - if (styleURL == url) { - return; - } - - styleRequest = nullptr; - styleURL = url; - styleJSON.clear(); - - style = std::make_unique<Style>(data, fileSource); - - const size_t pos = styleURL.rfind('/'); - std::string base = ""; - if (pos != std::string::npos) { - base = styleURL.substr(0, pos + 1); - } - - styleRequest = fileSource.request(Resource::style(styleURL), [this, base](Response res) { - if (res.error) { - if (res.error->reason == Response::Error::Reason::NotFound && - util::mapbox::isMapboxURL(styleURL)) { - Log::Error(Event::Setup, "style %s could not be found or is an incompatible legacy map or style", styleURL.c_str()); - } else { - Log::Error(Event::Setup, "loading style failed: %s", res.error->message.c_str()); - } - } else if (res.notModified || res.noContent) { - return; - } else { - loadStyleJSON(*res.data, base); - } - }); -} - -void MapContext::setStyleJSON(const std::string& json, const std::string& base) { - if (styleJSON == json) { - return; - } - - styleURL.clear(); - styleJSON.clear(); - - style = std::make_unique<Style>(data, fileSource); - - loadStyleJSON(json, base); -} - -void MapContext::loadStyleJSON(const std::string& json, const std::string& base) { - style->setJSON(json, base); - style->setObserver(this); - styleJSON = json; - - // force style cascade, causing all pending transitions to complete. - style->cascade(Clock::now()); - - updateAsync(Update::Classes | Update::RecalculateStyle | Update::Annotations); -} - -void MapContext::update() { - if (!style) { - updateFlags = Update::Nothing; - } - - if (updateFlags == Update::Nothing || (data.mode == MapMode::Still && !callback)) { - return; - } - - // This time point is used to: - // - Calculate style property transitions; - // - Hint style sources to notify when all its tiles are loaded; - frameData.timePoint = Clock::now(); - - if (style->loaded && updateFlags & Update::Annotations) { - data.getAnnotationManager()->updateStyle(*style); - updateFlags |= Update::Classes; - } - - if (updateFlags & Update::Classes) { - style->cascade(frameData.timePoint); - } - - if (updateFlags & Update::Classes || updateFlags & Update::RecalculateStyle) { - style->recalculate(transformState.getZoom(), frameData.timePoint); - } - - style->update(transformState, frameData.timePoint, *texturePool); - - if (data.mode == MapMode::Continuous) { - view.invalidate(); - } else if (callback && isLoaded()) { - view.activate(); - renderSync(transformState, frameData); - view.deactivate(); - } - - updateFlags = Update::Nothing; -} - -void MapContext::renderStill(const TransformState& state, const FrameData& frame, Map::StillImageCallback fn) { - if (!fn) { - Log::Error(Event::General, "StillImageCallback not set"); - return; - } - - if (data.mode != MapMode::Still) { - fn(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")), {}); - return; - } - - if (callback) { - fn(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")), {}); - return; - } - - if (!style) { - fn(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")), {}); - return; - } - - if (style->getLastError()) { - fn(style->getLastError(), {}); - return; - } - - callback = fn; - transformState = state; - frameData = frame; - - updateAsync(Update::RenderStill); -} - -bool MapContext::renderSync(const TransformState& state, const FrameData& frame) { - // Style was not loaded yet. - if (!style) { - return false; - } - - transformState = state; - frameData = frame; - - if (!painter) painter = std::make_unique<Painter>(data, transformState, glObjectStore); - painter->render(*style, frame, data.getAnnotationManager()->getSpriteAtlas()); - - if (data.mode == MapMode::Still) { - callback(nullptr, view.readStillImage()); - callback = nullptr; - } - - // Cleanup OpenGL objects that we abandoned since the last render call. - glObjectStore.performCleanup(); - - if (style->hasTransitions()) { - updateAsync(Update::RecalculateStyle); - } else if (painter->needsAnimation()) { - updateAsync(Update::Repaint); - } - - return isLoaded(); -} - -bool MapContext::isLoaded() const { - return style->isLoaded(); -} - -void MapContext::addAnnotationIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) { - data.getAnnotationManager()->addIcon(name, sprite); -} - -void MapContext::removeAnnotationIcon(const std::string& name) { - data.getAnnotationManager()->removeIcon(name); -} - -double MapContext::getTopOffsetPixelsForAnnotationIcon(const std::string& name) { - return data.getAnnotationManager()->getTopOffsetPixelsForIcon(name); -} - -void MapContext::addLayer(std::unique_ptr<StyleLayer> layer, optional<std::string> after) { - style->addLayer(std::move(layer), after); - updateAsync(Update::Classes); -} - -void MapContext::removeLayer(const std::string& id) { - style->removeLayer(id); - updateAsync(Update::Classes); -} - -std::vector<std::string> MapContext::getClasses() const { - return style->getClasses(); -} - -bool MapContext::hasClass(const std::string& className) const { - return style->hasClass(className); -} - -void MapContext::addClass(const std::string& className, const PropertyTransition& properties) { - if (style->addClass(className, properties)) updateAsync(Update::Classes); -} - -void MapContext::removeClass(const std::string& className, const PropertyTransition& properties) { - if (style->removeClass(className, properties)) updateAsync(Update::Classes); -} - -void MapContext::setClasses(const std::vector<std::string>& classNames, const PropertyTransition& properties) { - style->setClasses(classNames, properties); - updateAsync(Update::Classes); -} - -void MapContext::setSourceTileCacheSize(size_t size) { - if (size != sourceCacheSize) { - sourceCacheSize = size; - if (!style) return; - style->setSourceTileCacheSize(size); - view.invalidate(); - } -} - -void MapContext::onLowMemory() { - if (!style) return; - style->onLowMemory(); - view.invalidate(); -} - -void MapContext::onResourceLoaded() { - updateAsync(Update::Repaint); -} - -void MapContext::onResourceError(std::exception_ptr error) { - if (data.mode == MapMode::Still && callback) { - callback(error, {}); - callback = nullptr; - } -} - -void MapContext::dumpDebugLogs() const { - Log::Info(Event::General, "--------------------------------------------------------------------------------"); - Log::Info(Event::General, "MapContext::styleURL: %s", styleURL.c_str()); - if (style) { - style->dumpDebugLogs(); - } else { - Log::Info(Event::General, "no style loaded"); - } - Log::Info(Event::General, "--------------------------------------------------------------------------------"); -} - -} // namespace mbgl diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp deleted file mode 100644 index 93e7a52b25..0000000000 --- a/src/mbgl/map/map_context.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef MBGL_MAP_MAP_CONTEXT -#define MBGL_MAP_MAP_CONTEXT - -#include <mbgl/map/tile_id.hpp> -#include <mbgl/map/update.hpp> -#include <mbgl/map/transform_state.hpp> -#include <mbgl/map/map.hpp> -#include <mbgl/map/map_data.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/util/async_task.hpp> -#include <mbgl/util/ptr.hpp> -#include <mbgl/util/optional.hpp> -#include <mbgl/gl/gl_object_store.hpp> - -#include <vector> - -namespace mbgl { - -class View; -class MapData; -class Painter; -class SpriteImage; -class AsyncRequest; -class PropertyTransition; - -namespace gl { class TexturePool; } - -struct FrameData { - std::array<uint16_t, 2> framebufferSize; - TimePoint timePoint; -}; - -class MapContext : public Style::Observer { -public: - MapContext(View&, FileSource&, MapMode, GLContextMode, const float pixelRatio); - ~MapContext(); - - MapData& getData() { return data; } - - void triggerUpdate(const TransformState&, Update = Update::Nothing); - void renderStill(const TransformState&, const FrameData&, Map::StillImageCallback callback); - - // Triggers a synchronous render. Returns true if style has been fully loaded. - bool renderSync(const TransformState&, const FrameData&); - - void setStyleURL(const std::string&); - void setStyleJSON(const std::string& json, const std::string& base); - std::string getStyleURL() const { return styleURL; } - std::string getStyleJSON() const { return styleJSON; } - - bool isLoaded() const; - - // Annotations - void addAnnotationIcon(const std::string&, std::shared_ptr<const SpriteImage>); - void removeAnnotationIcon(const std::string&); - double getTopOffsetPixelsForAnnotationIcon(const std::string&); - void updateAnnotations(); - - // Style API - void addLayer(std::unique_ptr<StyleLayer>, - const optional<std::string> before); - void removeLayer(const std::string& id); - - void addClass(const std::string&, const PropertyTransition&); - void removeClass(const std::string&, const PropertyTransition&); - bool hasClass(const std::string&) const; - void setClasses(const std::vector<std::string>&, const PropertyTransition&); - std::vector<std::string> getClasses() const; - - void setSourceTileCacheSize(size_t size); - void onLowMemory(); - - void cleanup(); - void dumpDebugLogs() const; - -private: - void onResourceLoaded() override; - void onResourceError(std::exception_ptr) override; - - // Update the state indicated by the accumulated Update flags, then render. - void update(); - - // Helper function for triggering asynchronous updates. - void updateAsync(Update); - - // Loads the actual JSON object an creates a new Style object. - void loadStyleJSON(const std::string& json, const std::string& base); - - View& view; - FileSource& fileSource; - std::unique_ptr<MapData> dataPtr; - MapData& data; - - gl::GLObjectStore glObjectStore; - - Update updateFlags = Update::Nothing; - util::AsyncTask asyncUpdate; - - std::unique_ptr<gl::TexturePool> texturePool; - std::unique_ptr<Painter> painter; - std::unique_ptr<Style> style; - - std::string styleURL; - std::string styleJSON; - - std::unique_ptr<AsyncRequest> styleRequest; - - Map::StillImageCallback callback; - size_t sourceCacheSize; - TransformState transformState; - FrameData frameData; -}; - -} // namespace mbgl - -#endif diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index cf8be08756..301005a276 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -2,7 +2,6 @@ #include <mbgl/source/source.hpp> #include <mbgl/tile/tile.hpp> -#include <mbgl/map/map_context.hpp> #include <mbgl/map/map_data.hpp> #include <mbgl/platform/log.hpp> diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 89fb96ca4e..d13001a1a7 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -2,7 +2,6 @@ #define MBGL_RENDERER_PAINTER #include <mbgl/map/transform_state.hpp> -#include <mbgl/map/map_context.hpp> #include <mbgl/renderer/frame_history.hpp> #include <mbgl/renderer/bucket.hpp> @@ -13,6 +12,7 @@ #include <mbgl/gl/gl_config.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/gl/gl.hpp> @@ -23,9 +23,11 @@ #include <array> #include <vector> #include <set> +#include <map> namespace mbgl { +class MapData; class Style; class StyleLayer; class Tile; @@ -34,6 +36,7 @@ class GlyphAtlas; class LineAtlas; class Source; struct FrameData; +class TileData; class DebugBucket; class FillBucket; @@ -70,6 +73,11 @@ namespace util { class GLObjectStore; } +struct FrameData { + std::array<uint16_t, 2> framebufferSize; + TimePoint timePoint; +}; + class Painter : private util::noncopyable { public: Painter(MapData&, TransformState&, gl::GLObjectStore&); diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp index 5de7857bd2..6105fd4e73 100644 --- a/src/mbgl/renderer/painter_background.cpp +++ b/src/mbgl/renderer/painter_background.cpp @@ -3,6 +3,7 @@ #include <mbgl/layer/background_layer.hpp> #include <mbgl/shader/pattern_shader.hpp> #include <mbgl/shader/plain_shader.hpp> +#include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/util/mat4.hpp> #include <mbgl/util/tile_cover.hpp> diff --git a/test/map/map.cpp b/test/map/map.cpp index 2bc00b4334..2fdf63a79a 100644 --- a/test/map/map.cpp +++ b/test/map/map.cpp @@ -1,4 +1,5 @@ #include <mbgl/test/util.hpp> +#include <mbgl/test/stub_file_source.hpp> #include <mbgl/map/map.hpp> #include <mbgl/platform/default/headless_view.hpp> @@ -44,3 +45,15 @@ TEST(Map, Offline) { NetworkStatus::Set(NetworkStatus::Status::Online); } + +TEST(Map, DoubleStyleLoad) { + util::RunLoop runLoop; + + std::shared_ptr<HeadlessDisplay> display = std::make_shared<HeadlessDisplay>(); + HeadlessView view(display, 1, 512, 512); + StubFileSource fileSource; + + Map map(view, fileSource); + map.setStyleJSON("", ""); + map.setStyleJSON("", ""); +} diff --git a/test/map/map_context.cpp b/test/map/map_context.cpp deleted file mode 100644 index be7e2abb8a..0000000000 --- a/test/map/map_context.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include <mbgl/test/util.hpp> -#include <mbgl/test/stub_file_source.hpp> - -#include <mbgl/map/map_data.hpp> -#include <mbgl/map/map_context.hpp> -#include <mbgl/platform/default/headless_view.hpp> -#include <mbgl/platform/default/headless_display.hpp> -#include <mbgl/util/thread.hpp> -#include <mbgl/util/run_loop.hpp> - -using namespace mbgl; - -TEST(MapContext, DoubleStyleLoad) { - util::RunLoop loop; - - std::shared_ptr<HeadlessDisplay> display = std::make_shared<HeadlessDisplay>(); - HeadlessView view(display, 1, 512, 512); - StubFileSource fileSource; - - util::Thread<MapContext> context({"Map"}, - view, fileSource, MapMode::Continuous, GLContextMode::Unique, view.getPixelRatio()); - - context.invokeSync(&MapContext::setStyleJSON, "", ""); - context.invokeSync(&MapContext::setStyleJSON, "", ""); - context.invokeSync(&MapContext::cleanup); -} diff --git a/test/test.gypi b/test/test.gypi index b8b61ebf9c..6c2e8ad493 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -41,7 +41,7 @@ 'geometry/binpack.cpp', - 'map/map_context.cpp', + 'map/map.cpp', 'map/tile.cpp', 'map/transform.cpp', |