summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mbgl/map/map.hpp21
-rw-r--r--src/mbgl/map/map.cpp488
-rw-r--r--src/mbgl/map/map_context.cpp316
-rw-r--r--src/mbgl/map/map_context.hpp116
-rw-r--r--src/mbgl/renderer/painter.cpp1
-rw-r--r--src/mbgl/renderer/painter.hpp10
-rw-r--r--src/mbgl/renderer/painter_background.cpp1
-rw-r--r--test/map/map.cpp13
-rw-r--r--test/map/map_context.cpp26
-rw-r--r--test/test.gypi2
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',