diff options
author | Ansis Brammanis <brammanis@gmail.com> | 2015-02-04 11:05:36 -0800 |
---|---|---|
committer | Ansis Brammanis <brammanis@gmail.com> | 2015-02-04 11:05:36 -0800 |
commit | 465b1ac3eb45cbb946476ca73f421f02da225175 (patch) | |
tree | e7d29cf8f969250c1e94479b8778e52e10e0af9e /src | |
parent | c9f8de863dc93e4fd587290357c8a3b572a4f956 (diff) | |
parent | 002a709b67ac1ac36d4711a913a3fc082494ff71 (diff) | |
download | qtlocation-mapboxgl-465b1ac3eb45cbb946476ca73f421f02da225175.tar.gz |
Merge branch 'master' into pattern-functions
Conflicts:
src/mbgl/renderer/painter.cpp
src/mbgl/renderer/painter.hpp
src/mbgl/renderer/painter_fill.cpp
src/mbgl/renderer/painter_line.cpp
src/mbgl/style/style_layer.cpp
src/mbgl/style/style_layer.hpp
src/mbgl/style/style_layer_group.cpp
src/mbgl/style/style_layer_group.hpp
Diffstat (limited to 'src')
81 files changed, 1295 insertions, 1968 deletions
diff --git a/src/mbgl/geometry/buffer.hpp b/src/mbgl/geometry/buffer.hpp index 0b28f77d0a..3649574bbf 100644 --- a/src/mbgl/geometry/buffer.hpp +++ b/src/mbgl/geometry/buffer.hpp @@ -80,7 +80,7 @@ protected: } } pos += itemSize; - return static_cast<char *>(array) + (pos - itemSize); + return reinterpret_cast<char *>(array) + (pos - itemSize); } // Get a pointer to the item at a given index. @@ -92,7 +92,7 @@ protected: if (i * itemSize >= pos) { throw new std::runtime_error("Can't get element after array bounds"); } else { - return static_cast<char *>(array) + (i * itemSize); + return reinterpret_cast<char *>(array) + (i * itemSize); } } diff --git a/src/mbgl/geometry/elements_buffer.hpp b/src/mbgl/geometry/elements_buffer.hpp index 9255337cb5..5c1b421d35 100644 --- a/src/mbgl/geometry/elements_buffer.hpp +++ b/src/mbgl/geometry/elements_buffer.hpp @@ -21,11 +21,6 @@ struct ElementGroup : public util::noncopyable { : vertex_length(vertex_length_), elements_length(elements_length_) { } - - ElementGroup(ElementGroup &&rhs) noexcept - : array(std::move(rhs.array)), - vertex_length(rhs.vertex_length), - elements_length(rhs.elements_length) {}; }; class TriangleElementsBuffer : public Buffer< diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp index 35365c7e4d..93440b0e6c 100644 --- a/src/mbgl/geometry/sprite_atlas.cpp +++ b/src/mbgl/geometry/sprite_atlas.cpp @@ -66,13 +66,29 @@ bool SpriteAtlas::resize(const float newRatio) { } void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, const int src_y, - uint32_t *dst, const int dst_stride, const int dst_x, const int dst_y, - const int width, const int height) { - src += src_y * src_stride + src_x; - dst += dst_y * dst_stride + dst_x; - for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) { - for (int x = 0; x < width; x++) { - dst[x] = src[x]; + uint32_t *dst, const int dst_stride, const int dst_height, const int dst_x, const int dst_y, + const int width, const int height, const bool wrap) { + if (wrap) { + + for (int y = -1; y <= height; y++) { + int dst_y_wrapped = (y + dst_y + dst_height) % dst_height; + int src_y_wrapped = ((y + height) % height) + src_y; + int srcI = src_y_wrapped * src_stride + src_x; + int dstI = dst_y_wrapped * dst_stride; + for (int x = -1; x <= width; x++) { + int dst_x_wrapped = (x + dst_x + dst_stride) % dst_stride; + int src_x_wrapped = (x + width) % width; + dst[dstI + dst_x_wrapped] = src[srcI + src_x_wrapped]; + } + } + + } else { + dst += dst_y * dst_stride + dst_x; + src += src_y * src_stride + src_x; + for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) { + for (int x = 0; x < width; x++) { + dst[x] = src[x]; + } } } } @@ -97,7 +113,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width return rect; } -Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name) { +Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name, const bool wrap) { std::lock_guard<std::recursive_mutex> lock(mtx); auto rect_it = images.find(name); @@ -120,7 +136,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name) { images.emplace(name, rect); - copy(rect, pos); + copy(rect, pos, wrap); return rect; } @@ -128,7 +144,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name) { SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repeating) { std::lock_guard<std::recursive_mutex> lock(mtx); - Rect<dimension> rect = getImage(name); + Rect<dimension> rect = getImage(name, repeating); if (repeating) { // When the image is repeating, get the correct position of the image, rather than the // one rounded up to 4 pixels. @@ -157,7 +173,7 @@ void SpriteAtlas::allocate() { } } -void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src) { +void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, const bool wrap) { if (!sprite->raster) return; const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite->raster->getData()); if (!src_img) return; @@ -171,10 +187,12 @@ void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src) { /* source y */ src.y, /* dest buffer */ dst_img, /* dest stride */ width * pixelRatio, + /* dest height */ height * pixelRatio, /* dest x */ dst.x * pixelRatio, /* dest y */ dst.y * pixelRatio, /* icon dimension */ src.width, - /* icon dimension */ src.height + /* icon dimension */ src.height, + /* wrap padding */ wrap ); dirty = true; @@ -188,7 +206,7 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) { if (!sprite->isLoaded()) return; util::erase_if(uninitialized, [this](const std::string &name) { - Rect<dimension> dst = getImage(name); + Rect<dimension> dst = getImage(name, false); const SpritePosition& src = sprite->getSpritePosition(name); if (!src) { if (debug::spriteWarnings) { @@ -198,7 +216,7 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) { } if (src.width == dst.w * pixelRatio && src.height == dst.h * pixelRatio && src.pixelRatio == pixelRatio) { - copy(dst, src); + copy(dst, src, false); return true; } else { if (debug::spriteWarnings) { @@ -217,8 +235,8 @@ void SpriteAtlas::bind(bool linear) { #ifndef GL_ES_VERSION_2_0 MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); #endif - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); first = true; } else { MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); diff --git a/src/mbgl/geometry/sprite_atlas.hpp b/src/mbgl/geometry/sprite_atlas.hpp index ebfd69edf5..079c15cefd 100644 --- a/src/mbgl/geometry/sprite_atlas.hpp +++ b/src/mbgl/geometry/sprite_atlas.hpp @@ -46,7 +46,7 @@ public: // This getter attempts to read the image from the sprite if it is already loaded. // In that case, it copies it into the sprite atlas and returns the dimensions. // Otherwise, it returns a 0/0/0/0 rect. - Rect<dimension> getImage(const std::string& name); + Rect<dimension> getImage(const std::string& name, const bool wrap); SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false); @@ -66,7 +66,7 @@ public: private: void allocate(); Rect<SpriteAtlas::dimension> allocateImage(size_t width, size_t height); - void copy(const Rect<dimension>& dst, const SpritePosition& src); + void copy(const Rect<dimension>& dst, const SpritePosition& src, const bool wrap); std::recursive_mutex mtx; float pixelRatio = 1.0f; diff --git a/src/mbgl/geometry/vao.cpp b/src/mbgl/geometry/vao.cpp index 0868d92515..00976b4d54 100644 --- a/src/mbgl/geometry/vao.cpp +++ b/src/mbgl/geometry/vao.cpp @@ -4,6 +4,9 @@ namespace mbgl { +VertexArrayObject::VertexArrayObject() { +} + VertexArrayObject::~VertexArrayObject() { if (!gl::DeleteVertexArrays) return; diff --git a/src/mbgl/geometry/vao.hpp b/src/mbgl/geometry/vao.hpp index 2ecba731f7..72bacfd793 100644 --- a/src/mbgl/geometry/vao.hpp +++ b/src/mbgl/geometry/vao.hpp @@ -11,15 +11,8 @@ namespace mbgl { class VertexArrayObject : public util::noncopyable { public: - inline VertexArrayObject() {}; - - inline VertexArrayObject(VertexArrayObject &&rhs) noexcept - : vao(rhs.vao), - bound_shader(rhs.bound_shader), - bound_shader_name(rhs.bound_shader_name), - bound_vertex_buffer(rhs.bound_vertex_buffer), - bound_elements_buffer(rhs.bound_elements_buffer), - bound_offset(rhs.bound_offset) {}; + VertexArrayObject(); + ~VertexArrayObject(); template <typename Shader, typename VertexBuffer> inline void bind(Shader& shader, VertexBuffer &vertexBuffer, char *offset) { @@ -50,7 +43,9 @@ public: } } - ~VertexArrayObject(); + inline GLuint getID() const { + return vao; + } private: void bindVertexArrayObject(); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index ff584c54ba..5aff7a2b78 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -5,7 +5,6 @@ #include <mbgl/renderer/painter.hpp> #include <mbgl/map/sprite.hpp> #include <mbgl/util/transition.hpp> -#include <mbgl/util/time.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/clip_ids.hpp> #include <mbgl/util/string.hpp> @@ -25,6 +24,7 @@ #include <mbgl/platform/log.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/uv.hpp> +#include <mbgl/util/mapbox.hpp> #include <algorithm> #include <iostream> @@ -54,44 +54,12 @@ const static bool uvVersionCheck = []() { return true; }(); - -#include <zlib.h> -// Check zlib library version. -const static bool zlibVersionCheck = []() { - const char *const version = zlibVersion(); - if (version[0] != ZLIB_VERSION[0]) { - throw std::runtime_error(mbgl::util::sprintf<96>( - "zlib version mismatch: headers report %s, but library reports %s", ZLIB_VERSION, version)); - } - - return true; -}(); - - -#include <sqlite3.h> -// Check sqlite3 library version. -const static bool sqliteVersionCheck = []() { - if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER) { - throw std::runtime_error(mbgl::util::sprintf<96>( - "sqlite3 libversion mismatch: headers report %d, but library reports %d", - SQLITE_VERSION_NUMBER, sqlite3_libversion_number())); - } - if (strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID) != 0) { - throw std::runtime_error(mbgl::util::sprintf<256>( - "sqlite3 sourceid mismatch: headers report \"%s\", but library reports \"%s\"", - SQLITE_SOURCE_ID, sqlite3_sourceid())); - } - - return true; -}(); - - using namespace mbgl; Map::Map(View& view_, FileSource& fileSource_) : loop(util::make_unique<uv::loop>()), view(view_), -#ifndef NDEBUG +#ifdef DEBUG mainThread(std::this_thread::get_id()), mapThread(mainThread), #endif @@ -128,9 +96,7 @@ Map::~Map() { } uv::worker &Map::getWorker() { - if (!workers) { - workers = util::make_unique<uv::worker>(**loop, 4, "Tile Worker"); - } + assert(workers); return *workers; } @@ -154,8 +120,6 @@ void Map::start(bool startPaused) { workers.reset(); activeSources.clear(); - fileSource.clearLoop(); - terminating = true; // Closes all open handles on the loop. This means that the loop will automatically terminate. @@ -188,7 +152,7 @@ void Map::start(bool startPaused) { } thread = std::thread([this]() { -#ifndef NDEBUG +#ifdef DEBUG mapThread = std::this_thread::get_id(); #endif @@ -198,7 +162,7 @@ void Map::start(bool startPaused) { run(); -#ifndef NDEBUG +#ifdef DEBUG mapThread = std::thread::id(); #endif @@ -266,7 +230,7 @@ void Map::resume() { void Map::run() { if (mode == Mode::None) { -#ifndef NDEBUG +#ifdef DEBUG mapThread = mainThread; #endif mode = Mode::Static; @@ -278,6 +242,9 @@ void Map::run() { } view.activate(); + + workers = util::make_unique<uv::worker>(**loop, 4, "Tile Worker"); + setup(); prepare(); @@ -298,11 +265,10 @@ void Map::run() { // *after* all events have been processed. if (mode == Mode::Static) { render(); -#ifndef NDEBUG +#ifdef DEBUG mapThread = std::thread::id(); #endif mode = Mode::None; - fileSource.clearLoop(); } view.deactivate(); @@ -387,13 +353,14 @@ void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) { style = std::make_shared<Style>(); } + style->base = base; style->loadJSON((const uint8_t *)styleJSON.c_str()); style->cascadeClasses(classes); - fileSource.setBase(base); - glyphStore->setURL(style->glyph_url); - style->setDefaultTransitionDuration(defaultTransitionDuration); + const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken()); + glyphStore->setURL(glyphURL); + update(); } @@ -435,13 +402,13 @@ void Map::cancelTransitions() { #pragma mark - Position -void Map::moveBy(double dx, double dy, double duration) { - transform.moveBy(dx, dy, duration * 1_second); +void Map::moveBy(double dx, double dy, std::chrono::steady_clock::duration duration) { + transform.moveBy(dx, dy, duration); update(); } -void Map::setLonLat(double lon, double lat, double duration) { - transform.setLonLat(lon, lat, duration * 1_second); +void Map::setLonLat(double lon, double lat, std::chrono::steady_clock::duration duration) { + transform.setLonLat(lon, lat, duration); update(); } @@ -469,13 +436,13 @@ void Map::resetPosition() { #pragma mark - Scale -void Map::scaleBy(double ds, double cx, double cy, double duration) { - transform.scaleBy(ds, cx, cy, duration * 1_second); +void Map::scaleBy(double ds, double cx, double cy, std::chrono::steady_clock::duration duration) { + transform.scaleBy(ds, cx, cy, duration); update(); } -void Map::setScale(double scale, double cx, double cy, double duration) { - transform.setScale(scale, cx, cy, duration * 1_second); +void Map::setScale(double scale, double cx, double cy, std::chrono::steady_clock::duration duration) { + transform.setScale(scale, cx, cy, duration); update(); } @@ -483,8 +450,8 @@ double Map::getScale() const { return transform.getScale(); } -void Map::setZoom(double zoom, double duration) { - transform.setZoom(zoom, duration * 1_second); +void Map::setZoom(double zoom, std::chrono::steady_clock::duration duration) { + transform.setZoom(zoom, duration); update(); } @@ -492,8 +459,8 @@ double Map::getZoom() const { return transform.getZoom(); } -void Map::setLonLatZoom(double lon, double lat, double zoom, double duration) { - transform.setLonLatZoom(lon, lat, zoom, duration * 1_second); +void Map::setLonLatZoom(double lon, double lat, double zoom, std::chrono::steady_clock::duration duration) { + transform.setLonLatZoom(lon, lat, zoom, duration); update(); } @@ -526,13 +493,13 @@ double Map::getMaxZoom() const { #pragma mark - Rotation -void Map::rotateBy(double sx, double sy, double ex, double ey, double duration) { - transform.rotateBy(sx, sy, ex, ey, duration * 1_second); +void Map::rotateBy(double sx, double sy, double ex, double ey, std::chrono::steady_clock::duration duration) { + transform.rotateBy(sx, sy, ex, ey, duration); update(); } -void Map::setBearing(double degrees, double duration) { - transform.setAngle(-degrees * M_PI / 180, duration * 1_second); +void Map::setBearing(double degrees, std::chrono::steady_clock::duration duration) { + transform.setAngle(-degrees * M_PI / 180, duration); update(); } @@ -546,7 +513,7 @@ double Map::getBearing() const { } void Map::resetNorth() { - transform.setAngle(0, 500_milliseconds); + transform.setAngle(0, std::chrono::milliseconds(500)); update(); } @@ -560,6 +527,15 @@ void Map::stopRotating() { update(); } +#pragma mark - Access Token + +void Map::setAccessToken(const std::string &token) { + accessToken = token; +} + +const std::string &Map::getAccessToken() const { + return accessToken; +} #pragma mark - Toggles @@ -618,14 +594,14 @@ std::vector<std::string> Map::getClasses() const { return classes; } -void Map::setDefaultTransitionDuration(uint64_t milliseconds) { - defaultTransitionDuration = milliseconds; +void Map::setDefaultTransitionDuration(std::chrono::steady_clock::duration duration) { + defaultTransitionDuration = duration; if (style) { - style->setDefaultTransitionDuration(milliseconds); + style->setDefaultTransitionDuration(duration); } } -uint64_t Map::getDefaultTransitionDuration() { +std::chrono::steady_clock::duration Map::getDefaultTransitionDuration() { return defaultTransitionDuration; } @@ -675,20 +651,16 @@ void Map::updateTiles() { source->source->update(*this, getWorker(), style, *glyphAtlas, *glyphStore, *spriteAtlas, getSprite(), - *texturePool, fileSource, [this](){ update(); }); + *texturePool, fileSource, ***loop, [this](){ update(); }); } } void Map::prepare() { - if (!fileSource.hasLoop()) { - fileSource.setLoop(**loop); - } - if (!style) { style = std::make_shared<Style>(); - fileSource.request(ResourceType::JSON, styleURL)->onload([&](const Response &res) { - if (res.code == 200) { + fileSource.request({ Resource::Kind::JSON, styleURL}, **loop, [&](const Response &res) { + if (res.status == Response::Successful) { // Calculate the base const size_t pos = styleURL.rfind('/'); std::string base = ""; @@ -698,20 +670,20 @@ void Map::prepare() { setStyleJSON(res.data, base); } else { - Log::Error(Event::Setup, "loading style failed: %ld (%s)", res.code, res.message.c_str()); + Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str()); } }); } // Update transform transitions. - animationTime = util::now(); + animationTime = std::chrono::steady_clock::now(); if (transform.needsTransition()) { transform.updateTransitions(animationTime); } state = transform.currentState(); - animationTime = util::now(); + animationTime = std::chrono::steady_clock::now(); updateSources(); style->updateProperties(state.getNormalizedZoom(), animationTime); diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp index 6fac7862e7..84e9bb236a 100644 --- a/src/mbgl/map/raster_tile_data.cpp +++ b/src/mbgl/map/raster_tile_data.cpp @@ -5,8 +5,8 @@ using namespace mbgl; -RasterTileData::RasterTileData(Tile::ID const& id_, TexturePool& texturePool, const SourceInfo& source_) - : TileData(id_, source_), +RasterTileData::RasterTileData(Tile::ID const& id_, TexturePool& texturePool, const SourceInfo& source_, FileSource& fileSource_) + : TileData(id_, source_, fileSource_), bucket(texturePool, properties) { } diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp index 42070d9c61..7f338056f5 100644 --- a/src/mbgl/map/raster_tile_data.hpp +++ b/src/mbgl/map/raster_tile_data.hpp @@ -16,7 +16,7 @@ class RasterTileData : public TileData { friend class TileParser; public: - RasterTileData(Tile::ID const& id, TexturePool&, const SourceInfo&); + RasterTileData(Tile::ID const& id, TexturePool&, const SourceInfo&, FileSource &); ~RasterTileData(); virtual void parse(); diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 798cd41d1d..f23bcaa14a 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -15,6 +15,7 @@ #include <mbgl/geometry/glyph_atlas.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/platform/log.hpp> +#include <mbgl/util/uv_detail.hpp> #include <mbgl/map/vector_tile_data.hpp> #include <mbgl/map/raster_tile_data.hpp> @@ -39,8 +40,9 @@ void Source::load(Map& map, FileSource& fileSource) { util::ptr<Source> source = shared_from_this(); - fileSource.request(ResourceType::JSON, info.url)->onload([source, &map](const Response &res) { - if (res.code != 200) { + const std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken()); + fileSource.request({ Resource::Kind::JSON, url }, **map.loop, [source, &map](const Response &res) { + if (res.status != Response::Successful) { Log::Warning(Event::General, "failed to load source TileJSON"); return; } @@ -155,7 +157,7 @@ TileData::State Source::addTile(Map& map, uv::worker& worker, util::ptr<Style> style, GlyphAtlas& glyphAtlas, GlyphStore& glyphStore, SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite, - FileSource& fileSource, TexturePool& texturePool, + FileSource& fileSource, uv_loop_t &loop, TexturePool& texturePool, const Tile::ID& id, std::function<void ()> callback) { const TileData::State state = hasTile(id); @@ -188,14 +190,14 @@ TileData::State Source::addTile(Map& map, uv::worker& worker, new_tile.data = std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style, glyphAtlas, glyphStore, spriteAtlas, sprite, - texturePool, info); + texturePool, info, fileSource); } else if (info.type == SourceType::Raster) { - new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info); + new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, fileSource); } else { throw std::runtime_error("source type not implemented"); } - new_tile.data->request(worker, fileSource, map.getState().getPixelRatio(), callback); + new_tile.data->request(worker, loop, map.getState().getPixelRatio(), callback); tile_data.emplace(new_tile.data->id, new_tile.data); } @@ -286,7 +288,7 @@ void Source::update(Map& map, uv::worker& worker, util::ptr<Style> style, GlyphAtlas& glyphAtlas, GlyphStore& glyphStore, SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite, - TexturePool& texturePool, FileSource& fileSource, + TexturePool& texturePool, FileSource& fileSource, uv_loop_t& loop, std::function<void ()> callback) { if (!loaded || map.getTime() <= updated) return; @@ -310,7 +312,7 @@ void Source::update(Map& map, uv::worker& worker, const TileData::State state = addTile(map, worker, style, glyphAtlas, glyphStore, spriteAtlas, sprite, - fileSource, texturePool, + fileSource, loop, texturePool, id, callback); if (state != TileData::State::parsed) { diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 8976f67b05..3649837a58 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -6,7 +6,6 @@ #include <mbgl/style/style_source.hpp> #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/time.hpp> #include <mbgl/util/mat4.hpp> #include <mbgl/util/ptr.hpp> @@ -14,6 +13,7 @@ #include <forward_list> #include <iosfwd> #include <map> +#include <chrono> namespace mbgl { @@ -39,7 +39,7 @@ public: util::ptr<Style>, GlyphAtlas&, GlyphStore&, SpriteAtlas&, util::ptr<Sprite>, - TexturePool&, FileSource&, + TexturePool&, FileSource&, uv_loop_t& loop, std::function<void ()> callback); void updateMatrices(const mat4 &projMatrix, const TransformState &transform); @@ -63,7 +63,7 @@ private: util::ptr<Style>, GlyphAtlas&, GlyphStore&, SpriteAtlas&, util::ptr<Sprite>, - FileSource&, TexturePool&, + FileSource&, uv_loop_t &, TexturePool&, const Tile::ID&, std::function<void ()> callback); @@ -75,7 +75,7 @@ private: bool loaded = false; // Stores the time when this source was most recently updated. - timestamp updated = 0; + std::chrono::steady_clock::time_point updated = std::chrono::steady_clock::time_point::min(); std::map<Tile::ID, std::unique_ptr<Tile>> tiles; std::map<Tile::ID, std::weak_ptr<TileData>> tile_data; diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp index c1f71e59d9..19f7b7c8c6 100644 --- a/src/mbgl/map/sprite.cpp +++ b/src/mbgl/map/sprite.cpp @@ -52,6 +52,7 @@ Sprite::operator bool() const { // The reason this isn't part of the constructor is that calling shared_from_this() in // the constructor fails. void Sprite::load(FileSource& fileSource) { + if (!valid) { // Treat a non-existent sprite as a successfully loaded empty sprite. loadedImage = true; @@ -62,26 +63,26 @@ void Sprite::load(FileSource& fileSource) { util::ptr<Sprite> sprite = shared_from_this(); - fileSource.request(ResourceType::JSON, jsonURL)->onload([sprite](const Response &res) { - if (res.code == 200) { + fileSource.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) { + if (res.status == Response::Successful) { sprite->body = res.data; sprite->parseJSON(); sprite->complete(); } else { - Log::Warning(Event::Sprite, "Failed to load sprite info: Error %d: %s", res.code, res.message.c_str()); + Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str()); if (!sprite->future.valid()) { sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message))); } } }); - fileSource.request(ResourceType::Image, spriteURL)->onload([sprite](const Response &res) { - if (res.code == 200) { + fileSource.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) { + if (res.status == Response::Successful) { sprite->image = res.data; sprite->parseImage(); sprite->complete(); } else { - Log::Warning(Event::Sprite, "Failed to load sprite image: Error %d: %s", res.code, res.message.c_str()); + Log::Warning(Event::Sprite, "Failed to load sprite image: Error %s", res.message.c_str()); if (!sprite->future.valid()) { sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message))); } @@ -91,7 +92,6 @@ void Sprite::load(FileSource& fileSource) { void Sprite::complete() { if (loadedImage && loadedJSON) { - Log::Info(Event::Sprite, "loaded %s", spriteURL.c_str()); promise.set_value(); } } diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp index f89ff15baf..9d48041239 100644 --- a/src/mbgl/map/tile_data.cpp +++ b/src/mbgl/map/tile_data.cpp @@ -6,29 +6,33 @@ #include <mbgl/util/string.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/util/uv_detail.hpp> +#include <mbgl/platform/log.hpp> using namespace mbgl; -TileData::TileData(Tile::ID const& id_, const SourceInfo& source_) +TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& fileSource_) : id(id_), name(id), state(State::initial), source(source_), + fileSource(fileSource_), debugBucket(debugFontBuffer) { // Initialize tile debug coordinates debugFontBuffer.addText(name.c_str(), 50, 200, 5); } TileData::~TileData() { - cancel(); + if (req) { + fileSource.cancel(req); + } } const std::string TileData::toString() const { return std::string { "[tile " } + name + "]"; } -void TileData::request(uv::worker& worker, FileSource& fileSource, - float pixelRatio, std::function<void ()> callback) { +void TileData::request(uv::worker &worker, uv_loop_t &loop, + float pixelRatio, std::function<void()> callback) { if (source.tiles.empty()) return; @@ -51,8 +55,7 @@ void TileData::request(uv::worker& worker, FileSource& fileSource, // Note: Somehow this feels slower than the change to request_http() std::weak_ptr<TileData> weak_tile = shared_from_this(); - req = fileSource.request(ResourceType::Tile, url); - req->onload([weak_tile, url, callback, &worker](const Response &res) { + req = fileSource.request({ Resource::Kind::Tile, url }, &loop, [weak_tile, url, callback, &worker](const Response &res) { util::ptr<TileData> tile = weak_tile.lock(); if (!tile || tile->state == State::obsolete) { // noop. Tile is obsolete and we're now just waiting for the refcount @@ -61,9 +64,9 @@ void TileData::request(uv::worker& worker, FileSource& fileSource, } // Clear the request object. - tile->req.reset(); + tile->req = nullptr; - if (res.code == 200) { + if (res.status == Response::Successful) { tile->state = State::loaded; tile->data = res.data; @@ -71,9 +74,7 @@ void TileData::request(uv::worker& worker, FileSource& fileSource, // Schedule tile parsing in another thread tile->reparse(worker, callback); } else { -#if defined(DEBUG) - fprintf(stderr, "[%s] tile loading failed: %ld, %s\n", url.c_str(), res.code, res.message.c_str()); -#endif + Log::Error(Event::HttpRequest, "[%s] tile loading failed: %s", url.c_str(), res.message.c_str()); } }); } @@ -81,10 +82,10 @@ void TileData::request(uv::worker& worker, FileSource& fileSource, void TileData::cancel() { if (state != State::obsolete) { state = State::obsolete; - if (req) { - req->cancel(); - req.reset(); - } + } + if (req) { + fileSource.cancel(req); + req = nullptr; } } diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp index 1ae215b204..a83a4648dd 100644 --- a/src/mbgl/map/tile_data.hpp +++ b/src/mbgl/map/tile_data.hpp @@ -18,6 +18,8 @@ namespace uv { class worker; } +typedef struct uv_loop_s uv_loop_t; + namespace mbgl { class Map; @@ -46,10 +48,10 @@ public: }; public: - TileData(Tile::ID const& id, const SourceInfo&); + TileData(Tile::ID const& id, const SourceInfo&, FileSource&); ~TileData(); - void request(uv::worker&, FileSource&, float pixelRatio, std::function<void ()> callback); + void request(uv::worker&, uv_loop_t&, float pixelRatio, std::function<void ()> callback); void reparse(uv::worker&, std::function<void ()> callback); void cancel(); const std::string toString() const; @@ -71,9 +73,10 @@ public: public: const SourceInfo& source; + FileSource& fileSource; protected: - std::unique_ptr<Request> req; + Request *req = nullptr; std::string data; // Contains the tile ID string for painting debug information. diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp index e8c5a4320d..c3e7a8244b 100644 --- a/src/mbgl/map/tile_parser.cpp +++ b/src/mbgl/map/tile_parser.cpp @@ -91,6 +91,100 @@ void TileParser::parseStyleLayers(util::ptr<StyleLayerGroup> group) { } } +template <typename T> +struct PropertyEvaluator { + typedef T result_type; + PropertyEvaluator(float z_) : z(z_) {} + + template <typename P, typename std::enable_if<std::is_convertible<P, T>::value, int>::type = 0> + T operator()(const P &value) const { + return value; + } + + T operator()(const Function<T> &value) const { + return mapbox::util::apply_visitor(FunctionEvaluator<T>(z), value); + } + + template <typename P, typename std::enable_if<!std::is_convertible<P, T>::value, int>::type = 0> + T operator()(const P &) const { + return T(); + } + +private: + const float z; +}; + +template <typename T> +void TileParser::applyLayoutProperty(PropertyKey key, ClassProperties &classProperties, T &target, const float z) { + auto it = classProperties.properties.find(key); + if (it != classProperties.properties.end()) { + const PropertyEvaluator<T> evaluator(z); + target = mapbox::util::apply_visitor(evaluator, it->second); + + } +} + +template <> +void TileParser::applyLayoutProperties<FillProperties>(StyleBucket &bucket_desc, const float) { + bucket_desc.render.set<StyleBucketFill>(); + // no-op; Fill buckets don't currently have any applicable layout properties +} + +template<> +void TileParser::applyLayoutProperties<LineProperties>(StyleBucket &bucket_desc, const float z) { + bucket_desc.render.set<StyleBucketLine>(); + StyleBucketLine &line = bucket_desc.render.get<StyleBucketLine>(); + applyLayoutProperty(PropertyKey::LineCap, bucket_desc.layout, line.cap, z); + applyLayoutProperty(PropertyKey::LineJoin, bucket_desc.layout, line.join, z); + applyLayoutProperty(PropertyKey::LineMiterLimit, bucket_desc.layout, line.miter_limit, z); + applyLayoutProperty(PropertyKey::LineRoundLimit, bucket_desc.layout, line.round_limit, z); +} + +template<> +void TileParser::applyLayoutProperties<SymbolProperties>(StyleBucket &bucket_desc, const float z) { + bucket_desc.render.set<StyleBucketSymbol>(); + StyleBucketSymbol &symbol = bucket_desc.render.get<StyleBucketSymbol>(); + applyLayoutProperty(PropertyKey::SymbolPlacement, bucket_desc.layout, symbol.placement, z); + if (symbol.placement == PlacementType::Line) { + symbol.icon.rotation_alignment = RotationAlignmentType::Map; + symbol.text.rotation_alignment = RotationAlignmentType::Map; + }; + applyLayoutProperty(PropertyKey::SymbolMinDistance, bucket_desc.layout, symbol.min_distance, z); + applyLayoutProperty(PropertyKey::SymbolAvoidEdges, bucket_desc.layout, symbol.avoid_edges, z); + + applyLayoutProperty(PropertyKey::IconAllowOverlap, bucket_desc.layout, symbol.icon.allow_overlap, z); + applyLayoutProperty(PropertyKey::IconIgnorePlacement, bucket_desc.layout, symbol.icon.ignore_placement, z); + applyLayoutProperty(PropertyKey::IconOptional, bucket_desc.layout, symbol.icon.optional, z); + applyLayoutProperty(PropertyKey::IconRotationAlignment, bucket_desc.layout, symbol.icon.rotation_alignment, z); + applyLayoutProperty(PropertyKey::IconMaxSize, bucket_desc.layout, symbol.icon.max_size, z); + applyLayoutProperty(PropertyKey::IconImage, bucket_desc.layout, symbol.icon.image, z); + applyLayoutProperty(PropertyKey::IconPadding, bucket_desc.layout, symbol.icon.padding, z); + applyLayoutProperty(PropertyKey::IconRotate, bucket_desc.layout, symbol.icon.rotate, z); + applyLayoutProperty(PropertyKey::IconKeepUpright, bucket_desc.layout, symbol.icon.keep_upright, z); + applyLayoutProperty(PropertyKey::IconOffsetX, bucket_desc.layout, symbol.icon.offset[0], z); + applyLayoutProperty(PropertyKey::IconOffsetY, bucket_desc.layout, symbol.icon.offset[1], z); + + applyLayoutProperty(PropertyKey::TextRotationAlignment, bucket_desc.layout, symbol.text.rotation_alignment, z); + applyLayoutProperty(PropertyKey::TextField, bucket_desc.layout, symbol.text.field, z); + applyLayoutProperty(PropertyKey::TextFont, bucket_desc.layout, symbol.text.font, z); + applyLayoutProperty(PropertyKey::TextMaxSize, bucket_desc.layout, symbol.text.max_size, z); + applyLayoutProperty(PropertyKey::TextMaxWidth, bucket_desc.layout, symbol.text.max_width, z); + applyLayoutProperty(PropertyKey::TextLineHeight, bucket_desc.layout, symbol.text.line_height, z); + applyLayoutProperty(PropertyKey::TextLetterSpacing, bucket_desc.layout, symbol.text.letter_spacing, z); + applyLayoutProperty(PropertyKey::TextMaxAngle, bucket_desc.layout, symbol.text.max_angle, z); + applyLayoutProperty(PropertyKey::TextRotate, bucket_desc.layout, symbol.text.rotate, z); + applyLayoutProperty(PropertyKey::TextPadding, bucket_desc.layout, symbol.text.padding, z); + applyLayoutProperty(PropertyKey::TextIgnorePlacement, bucket_desc.layout, symbol.text.ignore_placement, z); + applyLayoutProperty(PropertyKey::TextOptional, bucket_desc.layout, symbol.text.optional, z); + applyLayoutProperty(PropertyKey::TextJustify, bucket_desc.layout, symbol.text.justify, z); + applyLayoutProperty(PropertyKey::TextAnchor, bucket_desc.layout, symbol.text.anchor, z); + applyLayoutProperty(PropertyKey::TextKeepUpright, bucket_desc.layout, symbol.text.keep_upright, z); + applyLayoutProperty(PropertyKey::TextTransform, bucket_desc.layout, symbol.text.transform, z); + applyLayoutProperty(PropertyKey::TextOffsetX, bucket_desc.layout, symbol.text.offset[0], z); + applyLayoutProperty(PropertyKey::TextOffsetY, bucket_desc.layout, symbol.text.offset[1], z); + applyLayoutProperty(PropertyKey::TextAllowOverlap, bucket_desc.layout, symbol.text.allow_overlap, z); +} + std::unique_ptr<Bucket> TileParser::createBucket(util::ptr<StyleBucket> bucket_desc) { if (!bucket_desc) { fprintf(stderr, "missing bucket desc\n"); @@ -105,13 +199,16 @@ std::unique_ptr<Bucket> TileParser::createBucket(util::ptr<StyleBucket> bucket_d auto layer_it = vector_data.layers.find(bucket_desc->source_layer); if (layer_it != vector_data.layers.end()) { const VectorTileLayer &layer = layer_it->second; - if (bucket_desc->render.is<StyleBucketFill>()) { + if (bucket_desc->type == StyleLayerType::Fill) { + applyLayoutProperties<FillProperties>(*bucket_desc, tile.id.z); return createFillBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketFill>()); - } else if (bucket_desc->render.is<StyleBucketLine>()) { + } else if (bucket_desc->type == StyleLayerType::Line) { + applyLayoutProperties<LineProperties>(*bucket_desc, tile.id.z); return createLineBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketLine>()); - } else if (bucket_desc->render.is<StyleBucketSymbol>()) { + } else if (bucket_desc->type == StyleLayerType::Symbol) { + applyLayoutProperties<SymbolProperties>(*bucket_desc, tile.id.z); return createSymbolBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketSymbol>()); - } else if (bucket_desc->render.is<StyleBucketRaster>()) { + } else if (bucket_desc->type == StyleLayerType::Raster) { return nullptr; } else { fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s' (source layer '%s')\n", bucket_desc->name.c_str(), bucket_desc->source_layer.c_str()); diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp index beae3af831..d10f9aa261 100644 --- a/src/mbgl/map/tile_parser.hpp +++ b/src/mbgl/map/tile_parser.hpp @@ -3,6 +3,8 @@ #include <mbgl/map/vector_tile.hpp> #include <mbgl/style/filter_expression.hpp> +#include <mbgl/style/class_properties.hpp> +#include <mbgl/style/style_bucket.hpp> #include <mbgl/text/glyph.hpp> #include <mbgl/util/ptr.hpp> #include <mbgl/util/noncopyable.hpp> @@ -48,6 +50,10 @@ public: private: bool obsolete() const; void parseStyleLayers(util::ptr<StyleLayerGroup> group); + + template <typename T> void applyLayoutProperties(StyleBucket &bucket, float z); + template <typename T> void applyLayoutProperty(PropertyKey key, ClassProperties &classProperties, T &, float z); + std::unique_ptr<Bucket> createBucket(util::ptr<StyleBucket> bucket_desc); std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill); diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index bf79d24b5d..7952531192 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -4,7 +4,6 @@ #include <mbgl/util/mat4.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/math.hpp> -#include <mbgl/util/time.hpp> #include <mbgl/util/transition.hpp> #include <mbgl/platform/platform.hpp> @@ -48,16 +47,16 @@ bool Transform::resize(const uint16_t w, const uint16_t h, const float ratio, #pragma mark - Position -void Transform::moveBy(const double dx, const double dy, const timestamp duration) { +void Transform::moveBy(const double dx, const double dy, const std::chrono::steady_clock::duration duration) { std::lock_guard<std::recursive_mutex> lock(mtx); _moveBy(dx, dy, duration); } -void Transform::_moveBy(const double dx, const double dy, const timestamp duration) { +void Transform::_moveBy(const double dx, const double dy, const std::chrono::steady_clock::duration duration) { // This is only called internally, so we don't need a lock here. - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionWillChangeAnimated : MapChangeRegionWillChange); @@ -66,25 +65,25 @@ void Transform::_moveBy(const double dx, const double dy, const timestamp durati constrain(final.scale, final.y); - if (duration == 0) { + if (duration == std::chrono::steady_clock::duration::zero()) { current.x = final.x; current.y = final.y; } else { // Use a common start time for all of the transitions to avoid divergent transitions. - timestamp start = util::now(); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); transitions.emplace_front( std::make_shared<util::ease_transition<double>>(current.x, final.x, current.x, start, duration)); transitions.emplace_front( std::make_shared<util::ease_transition<double>>(current.y, final.y, current.y, start, duration)); } - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionDidChangeAnimated : MapChangeRegionDidChange, duration); } -void Transform::setLonLat(const double lon, const double lat, const timestamp duration) { +void Transform::setLonLat(const double lon, const double lat, const std::chrono::steady_clock::duration duration) { std::lock_guard<std::recursive_mutex> lock(mtx); const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999); @@ -95,7 +94,7 @@ void Transform::setLonLat(const double lon, const double lat, const timestamp du } void Transform::setLonLatZoom(const double lon, const double lat, const double zoom, - const timestamp duration) { + const std::chrono::steady_clock::duration duration) { std::lock_guard<std::recursive_mutex> lock(mtx); double new_scale = std::pow(2.0, zoom); @@ -131,8 +130,8 @@ void Transform::startPanning() { // Add a 200ms timeout for resetting this to false current.panning = true; - timestamp start = util::now(); - pan_timeout = std::make_shared<util::timeout<bool>>(false, current.panning, start, 200_milliseconds); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + pan_timeout = std::make_shared<util::timeout<bool>>(false, current.panning, start, std::chrono::steady_clock::duration(200)); transitions.emplace_front(pan_timeout); } @@ -152,7 +151,7 @@ void Transform::_clearPanning() { #pragma mark - Zoom -void Transform::scaleBy(const double ds, const double cx, const double cy, const timestamp duration) { +void Transform::scaleBy(const double ds, const double cx, const double cy, const std::chrono::steady_clock::duration duration) { std::lock_guard<std::recursive_mutex> lock(mtx); // clamp scale to min/max values @@ -167,13 +166,13 @@ void Transform::scaleBy(const double ds, const double cx, const double cy, const } void Transform::setScale(const double scale, const double cx, const double cy, - const timestamp duration) { + const std::chrono::steady_clock::duration duration) { std::lock_guard<std::recursive_mutex> lock(mtx); _setScale(scale, cx, cy, duration); } -void Transform::setZoom(const double zoom, const timestamp duration) { +void Transform::setZoom(const double zoom, const std::chrono::steady_clock::duration duration) { std::lock_guard<std::recursive_mutex> lock(mtx); _setScale(std::pow(2.0, zoom), -1, -1, duration); @@ -198,8 +197,8 @@ void Transform::startScaling() { // Add a 200ms timeout for resetting this to false current.scaling = true; - timestamp start = util::now(); - scale_timeout = std::make_shared<util::timeout<bool>>(false, current.scaling, start, 200_milliseconds); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + scale_timeout = std::make_shared<util::timeout<bool>>(false, current.scaling, start, std::chrono::milliseconds(200)); transitions.emplace_front(scale_timeout); } @@ -231,7 +230,7 @@ void Transform::_clearScaling() { } } -void Transform::_setScale(double new_scale, double cx, double cy, const timestamp duration) { +void Transform::_setScale(double new_scale, double cx, double cy, const std::chrono::steady_clock::duration duration) { // This is only called internally, so we don't need a lock here. // Ensure that we don't zoom in further than the maximum allowed. @@ -265,10 +264,10 @@ void Transform::_setScale(double new_scale, double cx, double cy, const timestam } void Transform::_setScaleXY(const double new_scale, const double xn, const double yn, - const timestamp duration) { + const std::chrono::steady_clock::duration duration) { // This is only called internally, so we don't need a lock here. - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionWillChangeAnimated : MapChangeRegionWillChange); @@ -278,13 +277,13 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl constrain(final.scale, final.y); - if (duration == 0) { + if (duration == std::chrono::steady_clock::duration::zero()) { current.scale = final.scale; current.x = final.x; current.y = final.y; } else { // Use a common start time for all of the transitions to avoid divergent transitions. - timestamp start = util::now(); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); transitions.emplace_front(std::make_shared<util::ease_transition<double>>( current.scale, final.scale, current.scale, start, duration)); transitions.emplace_front( @@ -297,7 +296,7 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl Bc = s / 360; Cc = s / (2 * M_PI); - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionDidChangeAnimated : MapChangeRegionDidChange, duration); @@ -319,7 +318,7 @@ void Transform::constrain(double& scale, double& y) const { #pragma mark - Angle void Transform::rotateBy(const double start_x, const double start_y, const double end_x, - const double end_y, const timestamp duration) { + const double end_y, const std::chrono::steady_clock::duration duration) { std::lock_guard<std::recursive_mutex> lock(mtx); double center_x = current.width / 2, center_y = current.height / 2; @@ -349,7 +348,7 @@ void Transform::rotateBy(const double start_x, const double start_y, const doubl _setAngle(ang, duration); } -void Transform::setAngle(const double new_angle, const timestamp duration) { +void Transform::setAngle(const double new_angle, const std::chrono::steady_clock::duration duration) { std::lock_guard<std::recursive_mutex> lock(mtx); _setAngle(new_angle, duration); @@ -363,20 +362,20 @@ void Transform::setAngle(const double new_angle, const double cx, const double c if (cx >= 0 && cy >= 0) { dx = (final.width / 2) - cx; dy = (final.height / 2) - cy; - _moveBy(dx, dy, 0); + _moveBy(dx, dy, std::chrono::steady_clock::duration::zero()); } - _setAngle(new_angle, 0); + _setAngle(new_angle, std::chrono::steady_clock::duration::zero()); if (cx >= 0 && cy >= 0) { - _moveBy(-dx, -dy, 0); + _moveBy(-dx, -dy, std::chrono::steady_clock::duration::zero()); } } -void Transform::_setAngle(double new_angle, const timestamp duration) { +void Transform::_setAngle(double new_angle, const std::chrono::steady_clock::duration duration) { // This is only called internally, so we don't need a lock here. - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionWillChangeAnimated : MapChangeRegionWillChange); @@ -387,15 +386,15 @@ void Transform::_setAngle(double new_angle, const timestamp duration) { final.angle = new_angle; - if (duration == 0) { + if (duration == std::chrono::steady_clock::duration::zero()) { current.angle = final.angle; } else { - timestamp start = util::now(); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); transitions.emplace_front(std::make_shared<util::ease_transition<double>>( current.angle, final.angle, current.angle, start, duration)); } - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionDidChangeAnimated : MapChangeRegionDidChange, duration); @@ -414,8 +413,8 @@ void Transform::startRotating() { // Add a 200ms timeout for resetting this to false current.rotating = true; - timestamp start = util::now(); - rotate_timeout = std::make_shared<util::timeout<bool>>(false, current.rotating, start, 200_milliseconds); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + rotate_timeout = std::make_shared<util::timeout<bool>>(false, current.rotating, start, std::chrono::milliseconds(200)); transitions.emplace_front(rotate_timeout); } @@ -443,7 +442,7 @@ bool Transform::needsTransition() const { return !transitions.empty(); } -void Transform::updateTransitions(const timestamp now) { +void Transform::updateTransitions(const std::chrono::steady_clock::time_point now) { std::lock_guard<std::recursive_mutex> lock(mtx); transitions.remove_if([now](const util::ptr<util::transition> &transition) { diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index 06782057f6..646ad7318a 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -5,6 +5,7 @@ #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_bucket.hpp> #include <mbgl/geometry/glyph_atlas.hpp> +#include <mbgl/platform/log.hpp> using namespace mbgl; @@ -13,8 +14,8 @@ VectorTileData::VectorTileData(Tile::ID const& id_, GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_, SpriteAtlas& spriteAtlas_, util::ptr<Sprite> sprite_, TexturePool& texturePool_, - const SourceInfo& source_) - : TileData(id_, source_), + const SourceInfo& source_, FileSource &fileSource_) + : TileData(id_, source_, fileSource_), glyphAtlas(glyphAtlas_), glyphStore(glyphStore_), spriteAtlas(spriteAtlas_), @@ -35,6 +36,10 @@ void VectorTileData::parse() { } try { + if (!style) { + throw std::runtime_error("style isn't present in VectorTileData object anymore"); + } + // Parsing creates state that is encapsulated in TileParser. While parsing, // the TileParser object writes results into this objects. All other state // is going to be discarded afterwards. @@ -42,12 +47,13 @@ void VectorTileData::parse() { glyphAtlas, glyphStore, spriteAtlas, sprite, texturePool); + // Clear the style so that we don't have a cycle in the shared_ptr references. + style.reset(); + parser.parse(); } catch (const std::exception& ex) { -#if defined(DEBUG) - fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what()); -#endif - cancel(); + Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what()); + state = State::obsolete; return; } diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp index b9bf55a1b3..31318003af 100644 --- a/src/mbgl/map/vector_tile_data.hpp +++ b/src/mbgl/map/vector_tile_data.hpp @@ -36,7 +36,7 @@ public: GlyphAtlas&, GlyphStore&, SpriteAtlas&, util::ptr<Sprite>, TexturePool&, - const SourceInfo&); + const SourceInfo&, FileSource &); ~VectorTileData(); virtual void parse(); diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp index 3b55ca5a7d..b58e860b46 100644 --- a/src/mbgl/renderer/fill_bucket.cpp +++ b/src/mbgl/renderer/fill_bucket.cpp @@ -6,6 +6,7 @@ #include <mbgl/renderer/painter.hpp> #include <mbgl/style/style.hpp> #include <mbgl/map/vector_tile.hpp> +#include <mbgl/util/std.hpp> #include <mbgl/platform/gl.hpp> @@ -111,12 +112,13 @@ void FillBucket::tessellate() { throw geometry_too_long_exception(); } - if (!lineGroups.size() || (lineGroups.back().vertex_length + total_vertex_count > 65535)) { + if (!lineGroups.size() || (lineGroups.back()->vertex_length + total_vertex_count > 65535)) { // Move to a new group because the old one can't hold the geometry. - lineGroups.emplace_back(); + lineGroups.emplace_back(util::make_unique<line_group_type>()); } - line_group_type& lineGroup = lineGroups.back(); + assert(lineGroups.back()); + line_group_type& lineGroup = *lineGroups.back(); uint32_t lineIndex = lineGroup.vertex_length; for (const std::vector<ClipperLib::IntPoint>& polygon : polygons) { @@ -157,14 +159,15 @@ void FillBucket::tessellate() { } } - if (!triangleGroups.size() || (triangleGroups.back().vertex_length + total_vertex_count > 65535)) { + if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + total_vertex_count > 65535)) { // Move to a new group because the old one can't hold the geometry. - triangleGroups.emplace_back(); + triangleGroups.emplace_back(util::make_unique<triangle_group_type>()); } // We're generating triangle fans, so we always start with the first // coordinate in this polygon. - triangle_group_type& triangleGroup = triangleGroups.back(); + assert(triangleGroups.back()); + triangle_group_type& triangleGroup = *triangleGroups.back(); uint32_t triangleIndex = triangleGroup.vertex_length; for (int i = 0; i < triangle_count; ++i) { @@ -215,32 +218,35 @@ bool FillBucket::hasData() const { void FillBucket::drawElements(PlainShader& shader) { char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); - for (triangle_group_type& group : triangleGroups) { - group.array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * triangleElementsBuffer.itemSize; + for (auto& group : triangleGroups) { + assert(group); + group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * vertexBuffer.itemSize; + elements_index += group->elements_length * triangleElementsBuffer.itemSize; } } void FillBucket::drawElements(PatternShader& shader) { char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); - for (triangle_group_type& group : triangleGroups) { - group.array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * triangleElementsBuffer.itemSize; + for (auto& group : triangleGroups) { + assert(group); + group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * vertexBuffer.itemSize; + elements_index += group->elements_length * triangleElementsBuffer.itemSize; } } void FillBucket::drawVertices(OutlineShader& shader) { char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); char *elements_index = BUFFER_OFFSET(line_elements_start * lineElementsBuffer.itemSize); - for (line_group_type& group : lineGroups) { - group.array[0].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_LINES, group.elements_length * 2, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * lineElementsBuffer.itemSize; + for (auto& group : lineGroups) { + assert(group); + group->array[0].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_LINES, group->elements_length * 2, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * vertexBuffer.itemSize; + elements_index += group->elements_length * lineElementsBuffer.itemSize; } } diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/fill_bucket.hpp index ae766ec28d..e9762b7628 100644 --- a/src/mbgl/renderer/fill_bucket.hpp +++ b/src/mbgl/renderer/fill_bucket.hpp @@ -72,8 +72,8 @@ private: const size_t line_elements_start; VertexArrayObject array; - std::vector<triangle_group_type> triangleGroups; - std::vector<line_group_type> lineGroups; + std::vector<std::unique_ptr<triangle_group_type>> triangleGroups; + std::vector<std::unique_ptr<line_group_type>> lineGroups; std::vector<ClipperLib::IntPoint> line; bool hasVertices = false; diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp index 8b69162a23..21925fa312 100644 --- a/src/mbgl/renderer/frame_history.cpp +++ b/src/mbgl/renderer/frame_history.cpp @@ -3,11 +3,11 @@ using namespace mbgl; // Record frame history that will be used to calculate fading params -void FrameHistory::record(timestamp now, float zoom) { +void FrameHistory::record(std::chrono::steady_clock::time_point now, float zoom) { // first frame ever if (!history.size()) { - history.emplace_back(FrameSnapshot{0, zoom}); - history.emplace_back(FrameSnapshot{0, zoom}); + history.emplace_back(FrameSnapshot{std::chrono::steady_clock::time_point::min(), zoom}); + history.emplace_back(FrameSnapshot{std::chrono::steady_clock::time_point::min(), zoom}); } if (history.size() > 0 || history.back().z != zoom) { @@ -15,7 +15,7 @@ void FrameHistory::record(timestamp now, float zoom) { } } -bool FrameHistory::needsAnimation(const timestamp duration) const { +bool FrameHistory::needsAnimation(const std::chrono::steady_clock::duration duration) const { if (!history.size()) { return false; } @@ -26,7 +26,7 @@ bool FrameHistory::needsAnimation(const timestamp duration) const { const FrameSnapshot &pivot = history.back(); int i = -1; - while ((int)history.size() > i + 1 && history[i + 1].t + duration < pivot.t) { + while ((int)history.size() > i + 1 && history[i + 1].now + duration < pivot.now) { i++; } @@ -47,16 +47,16 @@ bool FrameHistory::needsAnimation(const timestamp duration) const { return false; } -FadeProperties FrameHistory::getFadeProperties(timestamp duration) +FadeProperties FrameHistory::getFadeProperties(std::chrono::steady_clock::duration duration) { - const timestamp currentTime = util::now(); + const std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now(); // Remove frames until only one is outside the duration, or until there are only three - while (history.size() > 3 && history[1].t + duration < currentTime) { + while (history.size() > 3 && history[1].now + duration < currentTime) { history.pop_front(); } - if (history[1].t + duration < currentTime) { + if (history[1].now + duration < currentTime) { history[0].z = history[1].z; } @@ -69,12 +69,13 @@ FadeProperties FrameHistory::getFadeProperties(timestamp duration) // Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one // duration - float zoomDiff = endingZ - history[1].z, timeDiff = lastFrame.t - history[1].t; + float zoomDiff = endingZ - history[1].z; + std::chrono::duration<float> timeDiff = lastFrame.now - history[1].now; float fadedist = zoomDiff / (timeDiff / duration); // At end of a zoom when the zoom stops changing continue pretending to zoom at that speed // bump is how much farther it would have been if it had continued zooming at the same rate - float bump = (currentTime - lastFrame.t) / duration * fadedist; + float bump = std::chrono::duration<float>(currentTime - lastFrame.now) / duration * fadedist; return FadeProperties { fadedist, diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp index 61bb59da33..f56b391256 100644 --- a/src/mbgl/renderer/frame_history.hpp +++ b/src/mbgl/renderer/frame_history.hpp @@ -4,15 +4,15 @@ #include <deque> #include <cassert> #include <cmath> +#include <chrono> #include <mbgl/platform/platform.hpp> -#include <mbgl/util/time.hpp> namespace mbgl { struct FrameSnapshot { - explicit inline FrameSnapshot(timestamp t_, float z_) : t(t_), z(z_) {} - float t; + explicit inline FrameSnapshot(std::chrono::steady_clock::time_point now_, float z_) : now(now_), z(z_) {} + std::chrono::steady_clock::time_point now; float z; }; @@ -26,10 +26,10 @@ struct FadeProperties { class FrameHistory { public: // Record frame history that will be used to calculate fading params - void record(timestamp now, float zoom); + void record(std::chrono::steady_clock::time_point now, float zoom); - bool needsAnimation(timestamp duration) const; - FadeProperties getFadeProperties(timestamp duration); + bool needsAnimation(std::chrono::steady_clock::duration duration) const; + FadeProperties getFadeProperties(std::chrono::steady_clock::duration duration); public: std::deque<FrameSnapshot> history; diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp index 4937c8ac63..8141e68e24 100644 --- a/src/mbgl/renderer/line_bucket.cpp +++ b/src/mbgl/renderer/line_bucket.cpp @@ -7,6 +7,7 @@ #include <mbgl/map/vector_tile.hpp> #include <mbgl/util/math.hpp> +#include <mbgl/util/std.hpp> #include <mbgl/platform/gl.hpp> #define BUFFER_OFFSET(i) ((char *)nullptr + (i)) @@ -306,12 +307,13 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { // Store the triangle/line groups. { - if (!triangleGroups.size() || (triangleGroups.back().vertex_length + vertex_count > 65535)) { + if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + vertex_count > 65535)) { // Move to a new group because the old one can't hold the geometry. - triangleGroups.emplace_back(); + triangleGroups.emplace_back(util::make_unique<triangle_group_type>()); } - triangle_group_type& group = triangleGroups.back(); + assert(triangleGroups.back()); + triangle_group_type& group = *triangleGroups.back(); for (const TriangleElement& triangle : triangle_store) { triangleElementsBuffer.add( group.vertex_length + triangle.a, @@ -326,12 +328,13 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { // Store the line join/cap groups. { - if (!pointGroups.size() || (pointGroups.back().vertex_length + vertex_count > 65535)) { + if (!pointGroups.size() || (pointGroups.back()->vertex_length + vertex_count > 65535)) { // Move to a new group because the old one can't hold the geometry. - pointGroups.emplace_back(); + pointGroups.emplace_back(util::make_unique<point_group_type>()); } - point_group_type& group = pointGroups.back(); + assert(pointGroups.back()); + point_group_type& group = *pointGroups.back(); for (PointElement point : point_store) { pointElementsBuffer.add(group.vertex_length + point); } @@ -351,8 +354,9 @@ bool LineBucket::hasData() const { bool LineBucket::hasPoints() const { if (!pointGroups.empty()) { - for (const point_group_type& group : pointGroups) { - if (group.elements_length) { + for (const auto& group : pointGroups) { + assert(group); + if (group->elements_length) { return true; } } @@ -363,55 +367,59 @@ bool LineBucket::hasPoints() const { void LineBucket::drawLines(LineShader& shader) { char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); - for (triangle_group_type& group : triangleGroups) { - if (!group.elements_length) { + for (auto& group : triangleGroups) { + assert(group); + if (!group->elements_length) { continue; } - group.array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * triangleElementsBuffer.itemSize; + group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * vertexBuffer.itemSize; + elements_index += group->elements_length * triangleElementsBuffer.itemSize; } } void LineBucket::drawLineSDF(LineSDFShader& shader) { char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); - for (triangle_group_type& group : triangleGroups) { - if (!group.elements_length) { + for (auto& group : triangleGroups) { + assert(group); + if (!group->elements_length) { continue; } - group.array[2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * triangleElementsBuffer.itemSize; + group->array[2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * vertexBuffer.itemSize; + elements_index += group->elements_length * triangleElementsBuffer.itemSize; } } void LineBucket::drawLinePatterns(LinepatternShader& shader) { char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); - for (triangle_group_type& group : triangleGroups) { - if (!group.elements_length) { + for (auto& group : triangleGroups) { + assert(group); + if (!group->elements_length) { continue; } - group.array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * triangleElementsBuffer.itemSize; + group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * vertexBuffer.itemSize; + elements_index += group->elements_length * triangleElementsBuffer.itemSize; } } void LineBucket::drawPoints(LinejoinShader& shader) { char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); char *elements_index = BUFFER_OFFSET(point_elements_start * pointElementsBuffer.itemSize); - for (point_group_type& group : pointGroups) { - if (!group.elements_length) { + for (auto& group : pointGroups) { + assert(group); + if (!group->elements_length) { continue; } - group.array[0].bind(shader, vertexBuffer, pointElementsBuffer, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_POINTS, group.elements_length, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * pointElementsBuffer.itemSize; + group->array[0].bind(shader, vertexBuffer, pointElementsBuffer, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_POINTS, group->elements_length, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * vertexBuffer.itemSize; + elements_index += group->elements_length * pointElementsBuffer.itemSize; } } diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp index c4d0d4a050..3bc8bf399d 100644 --- a/src/mbgl/renderer/line_bucket.hpp +++ b/src/mbgl/renderer/line_bucket.hpp @@ -55,8 +55,8 @@ private: const size_t triangle_elements_start; const size_t point_elements_start; - std::vector<triangle_group_type> triangleGroups; - std::vector<point_group_type> pointGroups; + std::vector<std::unique_ptr<triangle_group_type>> triangleGroups; + std::vector<std::unique_ptr<point_group_type>> pointGroups; }; } diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 02e263a1ca..78b6c18e4a 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -5,7 +5,6 @@ #include <mbgl/style/style_bucket.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/string.hpp> -#include <mbgl/util/time.hpp> #include <mbgl/util/clip_ids.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/mat3.hpp> @@ -35,7 +34,7 @@ Painter::~Painter() { } bool Painter::needsAnimation() const { - return frameHistory.needsAnimation(300); + return frameHistory.needsAnimation(std::chrono::milliseconds(300)); } void Painter::setup() { @@ -216,7 +215,7 @@ void Painter::prepareTile(const Tile& tile) { } void Painter::render(const Style& style, const std::set<util::ptr<StyleSource>>& sources, - TransformState state_, timestamp time) { + TransformState state_, std::chrono::steady_clock::time_point time) { state = state_; clear(); diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 4d4876a6f1..f38a9f0559 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -28,6 +28,7 @@ #include <map> #include <unordered_map> #include <set> +#include <chrono> namespace mbgl { @@ -78,7 +79,7 @@ public: void render(const Style& style, const std::set<util::ptr<StyleSource>>& sources, TransformState state, - timestamp time); + std::chrono::steady_clock::time_point time); void renderLayers(util::ptr<StyleLayerGroup> group); void renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id = nullptr, const mat4* matrix = nullptr); diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index d9e4a58c23..579a6d2e06 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -5,6 +5,7 @@ #include <mbgl/geometry/sprite_atlas.hpp> #include <mbgl/map/map.hpp> #include <mbgl/util/math.hpp> + #include <cmath> using namespace mbgl; @@ -52,7 +53,7 @@ void Painter::renderSDF(SymbolBucket &bucket, sdfShader.u_angle = (int32_t)(angle + 256) % 256; sdfShader.u_zoom = (state.getNormalizedZoom() - zoomAdjust) * 10; // current zoom level - FadeProperties f = frameHistory.getFadeProperties(300_milliseconds); + FadeProperties f = frameHistory.getFadeProperties(std::chrono::milliseconds(300)); sdfShader.u_fadedist = f.fadedist * 10; sdfShader.u_minfadezoom = std::floor(f.minfadezoom * 10); sdfShader.u_maxfadezoom = std::floor(f.maxfadezoom * 10); diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index c6a7c8c4e1..f53bbfa954 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -177,13 +177,13 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress if (feature.label.length()) { shaping = fontStack.getShaping( /* string */ feature.label, - /* maxWidth */ properties.text.max_width, - /* lineHeight */ properties.text.line_height, + /* maxWidth: ems */ properties.text.max_width * 24, + /* lineHeight: ems */ properties.text.line_height * 24, /* horizontalAlign */ horizontalAlign, /* verticalAlign */ verticalAlign, /* justify */ justify, - /* spacing */ properties.text.letter_spacing, - /* translate */ properties.text.offset); + /* spacing: ems */ properties.text.letter_spacing * 24, + /* translate */ vec2<float>(properties.text.offset[0], properties.text.offset[1])); // Add the glyphs we need for this label to the glyph atlas. if (shaping.size()) { @@ -195,7 +195,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress // if feature has icon, get sprite atlas position if (feature.sprite.length()) { sprite.waitUntilLoaded(); - image = spriteAtlas.getImage(feature.sprite); + image = spriteAtlas.getImage(feature.sprite, false); if (sprite.getSpritePosition(feature.sprite).sdf) { sdfIcons = true; @@ -322,19 +322,19 @@ void SymbolBucket::addFeature(const std::vector<Coordinate> &line, const Shaping collision.insert(glyphPlacement.boxes, anchor, glyphScale, glyphRange, horizontalText); } - if (inside) addSymbols(text, glyphPlacement.shapes, glyphScale, glyphRange); + if (inside) addSymbols<TextBuffer, TextElementGroup>(text, glyphPlacement.shapes, glyphScale, glyphRange); } if (iconScale && std::isfinite(iconScale)) { if (!properties.icon.ignore_placement) { collision.insert(iconPlacement.boxes, anchor, iconScale, iconRange, horizontalIcon); } - if (inside) addSymbols(icon, iconPlacement.shapes, iconScale, iconRange); + if (inside) addSymbols<IconBuffer, IconElementGroup>(icon, iconPlacement.shapes, iconScale, iconRange); } } } -template <typename Buffer> +template <typename Buffer, typename GroupType> void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale, PlacementRange placementRange) { const float zoom = collision.zoom; @@ -366,14 +366,15 @@ void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float const int glyph_vertex_length = 4; if (!buffer.groups.size() || - (buffer.groups.back().vertex_length + glyph_vertex_length > 65535)) { + (buffer.groups.back()->vertex_length + glyph_vertex_length > 65535)) { // Move to a new group because the old one can't hold the geometry. - buffer.groups.emplace_back(); + buffer.groups.emplace_back(util::make_unique<GroupType>()); } // We're generating triangle fans, so we always start with the first // coordinate in this polygon. - auto &triangleGroup = buffer.groups.back(); + assert(buffer.groups.back()); + auto &triangleGroup = *buffer.groups.back(); uint32_t triangleIndex = triangleGroup.vertex_length; // coordinates (2 triangles) @@ -398,33 +399,36 @@ void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float void SymbolBucket::drawGlyphs(SDFShader &shader) { char *vertex_index = BUFFER_OFFSET(0); char *elements_index = BUFFER_OFFSET(0); - for (TextElementGroup &group : text.groups) { - group.array[0].bind(shader, text.vertices, text.triangles, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * text.vertices.itemSize; - elements_index += group.elements_length * text.triangles.itemSize; + for (auto &group : text.groups) { + assert(group); + group->array[0].bind(shader, text.vertices, text.triangles, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * text.vertices.itemSize; + elements_index += group->elements_length * text.triangles.itemSize; } } void SymbolBucket::drawIcons(SDFShader &shader) { char *vertex_index = BUFFER_OFFSET(0); char *elements_index = BUFFER_OFFSET(0); - for (IconElementGroup &group : icon.groups) { - group.array[0].bind(shader, icon.vertices, icon.triangles, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * icon.vertices.itemSize; - elements_index += group.elements_length * icon.triangles.itemSize; + for (auto &group : icon.groups) { + assert(group); + group->array[0].bind(shader, icon.vertices, icon.triangles, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * icon.vertices.itemSize; + elements_index += group->elements_length * icon.triangles.itemSize; } } void SymbolBucket::drawIcons(IconShader &shader) { char *vertex_index = BUFFER_OFFSET(0); char *elements_index = BUFFER_OFFSET(0); - for (IconElementGroup &group : icon.groups) { - group.array[1].bind(shader, icon.vertices, icon.triangles, vertex_index); - MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); - vertex_index += group.vertex_length * icon.vertices.itemSize; - elements_index += group.elements_length * icon.triangles.itemSize; + for (auto &group : icon.groups) { + assert(group); + group->array[1].bind(shader, icon.vertices, icon.triangles, vertex_index); + MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index)); + vertex_index += group->vertex_length * icon.vertices.itemSize; + elements_index += group->elements_length * icon.triangles.itemSize; } } } diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index bc148579fa..537c6d2097 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -81,7 +81,7 @@ private: // Adds placed items to the buffer. - template <typename Buffer> + template <typename Buffer, typename GroupType> void addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale, PlacementRange placementRange); // Adds glyphs to the glyph atlas so that they have a left/top/width/height coordinates associated to them that we can use for writing to a buffer. @@ -95,16 +95,16 @@ public: private: Collision &collision; - struct { + struct TextBuffer { TextVertexBuffer vertices; TriangleElementsBuffer triangles; - std::vector<TextElementGroup> groups; + std::vector<std::unique_ptr<TextElementGroup>> groups; } text; - struct { + struct IconBuffer { IconVertexBuffer vertices; TriangleElementsBuffer triangles; - std::vector<IconElementGroup> groups; + std::vector<std::unique_ptr<IconElementGroup>> groups; } icon; }; diff --git a/src/mbgl/storage/asset_request.hpp b/src/mbgl/storage/asset_request.hpp deleted file mode 100644 index 3114d41ad2..0000000000 --- a/src/mbgl/storage/asset_request.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MBGL_STORAGE_ASSET_REQUEST -#define MBGL_STORAGE_ASSET_REQUEST - -#include <mbgl/storage/base_request.hpp> - -namespace mbgl { - -typedef struct uv_loop_s uv_loop_t; - -struct AssetRequestBaton; - -class AssetRequest : public BaseRequest { -public: - AssetRequest(const std::string &path, uv_loop_t *loop); - ~AssetRequest(); - - void cancel(); - -private: - AssetRequestBaton *ptr = nullptr; - - friend struct AssetRequestBaton; -}; - -} - -#endif diff --git a/src/mbgl/storage/base_request.cpp b/src/mbgl/storage/base_request.cpp deleted file mode 100644 index 510bd7bf1c..0000000000 --- a/src/mbgl/storage/base_request.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include <mbgl/storage/base_request.hpp> -#include <mbgl/storage/response.hpp> -#include <mbgl/storage/request.hpp> -#include <mbgl/util/std.hpp> - -#include <uv.h> - -#include <cassert> - -namespace mbgl { - -template <typename T, typename ...Args> -void invoke(const std::forward_list<std::unique_ptr<Callback>> &list, Args&& ...args) { - for (const std::unique_ptr<Callback> &callback : list) { - assert(callback); - if (callback->is<T>()) { - callback->get<T>()(::std::forward<Args>(args)...); - } - } -} - -BaseRequest::BaseRequest(const std::string &path_) : threadId(std::this_thread::get_id()), path(path_) { -} - -// A base request can only be "canceled" by destroying the object. In that case, we'll have to -// notify all cancel callbacks. -BaseRequest::~BaseRequest() { - assert(std::this_thread::get_id() == threadId); - notify(); -} - -void BaseRequest::retryImmediately() { - // no-op. override in child class. -} - -void BaseRequest::notify() { - assert(std::this_thread::get_id() == threadId); - - // The parameter exists solely so that any calls to ->remove() - // are not going to cause deallocation of this object while this call is in progress. - util::ptr<BaseRequest> retain = self; - - // Swap the lists so that it's safe for callbacks to call ->cancel() - // on the request object, which would modify the list. - const std::forward_list<std::unique_ptr<Callback>> list = std::move(callbacks); - callbacks.clear(); - - if (response) { - invoke<CompletedCallback>(list, *response); - } else { - invoke<AbortedCallback>(list); - } - - self.reset(); -} - -Callback *BaseRequest::add(Callback &&callback, const util::ptr<BaseRequest> &request) { - assert(std::this_thread::get_id() == threadId); - assert(this == request.get()); - - if (response) { - // We already have a response. Notify right away. - if (callback.is<CompletedCallback>()) { - callback.get<CompletedCallback>()(*response); - } else { - // We already know that this request was successful. The AbortedCallback will be discarded - // here since it would never be called. - } - return nullptr; - } else { - self = request; - callbacks.push_front(util::make_unique<Callback>(std::move(callback))); - return callbacks.front().get(); - } -} - -void BaseRequest::remove(Callback *callback) { - assert(std::this_thread::get_id() == threadId); - callbacks.remove_if([=](const std::unique_ptr<Callback> &cb) { - return cb.get() == callback; - }); - if (callbacks.empty()) { - self.reset(); - } -} - -} diff --git a/src/mbgl/storage/base_request.hpp b/src/mbgl/storage/base_request.hpp deleted file mode 100644 index 5119c343e9..0000000000 --- a/src/mbgl/storage/base_request.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef MBGL_STORAGE_BASE_REQUEST -#define MBGL_STORAGE_BASE_REQUEST - -#include <mbgl/storage/request_callback.hpp> -#include <mbgl/util/ptr.hpp> - -#include <string> -#include <forward_list> -#include <functional> -#include <thread> - -typedef struct uv_loop_s uv_loop_t; -typedef struct uv_async_s uv_async_t; - -namespace mbgl { - -class Response; -class Request; - -class BaseRequest { -private: - // Make noncopyable and immovable - BaseRequest(const BaseRequest &) = delete; - BaseRequest(BaseRequest &&) = delete; - BaseRequest& operator=(const BaseRequest &) = delete; - BaseRequest& operator=(BaseRequest &&) = delete; - -public: - BaseRequest(const std::string &path); - virtual ~BaseRequest(); - - Callback *add(Callback &&callback, const util::ptr<BaseRequest> &request); - void remove(Callback *callback); - - // Must be called by subclasses when a valid Response object is available. It will notify - // all listeners. - void notify(); - - // This function is called when the request ought to be stopped. Any subclass must make sure this - // is also called in its destructor. Calling this function repeatedly must be safe. - // This function must call notify(). - virtual void cancel() = 0; - - // This function is called when the request should be reattempted immediately. This is typically - // reaction to a network status change. - virtual void retryImmediately(); - -public: - const std::thread::id threadId; - const std::string path; - std::unique_ptr<Response> response; - -protected: - // This object may hold a shared_ptr to itself. It does this to prevent destruction of this object - // while a request is in progress. - util::ptr<BaseRequest> self; - std::forward_list<std::unique_ptr<Callback>> callbacks; -}; - -} - -#endif diff --git a/src/mbgl/storage/caching_http_file_source.cpp b/src/mbgl/storage/caching_http_file_source.cpp deleted file mode 100644 index 634a56f9c4..0000000000 --- a/src/mbgl/storage/caching_http_file_source.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include <mbgl/storage/caching_http_file_source.hpp> -#include <mbgl/storage/asset_request.hpp> -#include <mbgl/storage/http_request.hpp> -#include <mbgl/storage/sqlite_store.hpp> -#include <mbgl/storage/asset_request.hpp> -#include <mbgl/util/uv-messenger.h> -#include <mbgl/util/mapbox.hpp> -#include <mbgl/util/std.hpp> - -#include <uv.h> - -namespace mbgl { - -CachingHTTPFileSource::CachingHTTPFileSource(const std::string &path_) - : path(path_) {} - -CachingHTTPFileSource::~CachingHTTPFileSource() { -} - -void CachingHTTPFileSource::setLoop(uv_loop_t* loop_) { - assert(!loop); - - threadId = std::this_thread::get_id(); - store = !path.empty() ? util::ptr<SQLiteStore>(new SQLiteStore(loop_, path)) : nullptr; - loop = loop_; - queue = new uv_messenger_t; - - uv_messenger_init(loop, queue, [](void *ptr) { - std::unique_ptr<std::function<void()>> fn { reinterpret_cast<std::function<void()> *>(ptr) }; - (*fn)(); - }); - uv_unref((uv_handle_t *)&queue->async); -} - -bool CachingHTTPFileSource::hasLoop() { - return loop; -} - -void CachingHTTPFileSource::clearLoop() { - assert(std::this_thread::get_id() == threadId); - assert(loop); - - uv_messenger_stop(queue, [](uv_messenger_t *msgr) { - delete msgr; - }); - - util::ptr<BaseRequest> req; - - // Send a cancel() message to all requests that we are still holding. - for (const std::pair<std::string, std::weak_ptr<BaseRequest>> &pair : pending) { - if ((req = pair.second.lock())) { - req->cancel(); - } - } - - store.reset(); - - loop = nullptr; -} - -void CachingHTTPFileSource::setBase(std::string value) { - // TODO: Make threadsafe. - base.swap(value); -} - -void CachingHTTPFileSource::setAccessToken(std::string value) { - // TODO: Make threadsafe. - accessToken.swap(value); -} - -std::string CachingHTTPFileSource::getAccessToken() const { - return accessToken; -} - -std::unique_ptr<Request> CachingHTTPFileSource::request(ResourceType type, const std::string& url_) { - assert(std::this_thread::get_id() == threadId); - - std::string url = url_; - - // Make URL absolute. - const size_t separator = url.find("://"); - if (separator == std::string::npos) { - url = base + url; - } - - // Normalize mapbox:// URLs. - switch (type) { - case ResourceType::Glyphs: - url = util::mapbox::normalizeGlyphsURL(url, accessToken); - default: - url = util::mapbox::normalizeSourceURL(url, accessToken); - } - - util::ptr<BaseRequest> req; - - // First, try to find an existing Request object. - auto it = pending.find(url); - if (it != pending.end()) { - req = it->second.lock(); - } - - if (!req) { - if (url.substr(0, 8) == "asset://") { - req = std::make_shared<AssetRequest>(url.substr(8), loop); - } else { - req = std::make_shared<HTTPRequest>(type, url, loop, store); - } - - pending.emplace(url, req); - } - - return util::make_unique<Request>(req); -} - -void CachingHTTPFileSource::prepare(std::function<void()> fn) { - if (std::this_thread::get_id() == threadId) { - fn(); - } else { - uv_messenger_send(queue, new std::function<void()>(std::move(fn))); - } -} - -void CachingHTTPFileSource::setReachability(bool reachable) { - if (reachable && loop) { - prepare([this]() { - util::ptr<BaseRequest> req; - for (const std::pair<std::string, std::weak_ptr<BaseRequest>> &pair : pending) { - if ((req = pair.second.lock())) { - req->retryImmediately(); - } - } - }); - } -} - -} diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp new file mode 100644 index 0000000000..e40aaff649 --- /dev/null +++ b/src/mbgl/storage/default_file_source.cpp @@ -0,0 +1,245 @@ +#include <mbgl/storage/default_file_source.hpp> +#include <mbgl/storage/default/request.hpp> +#include <mbgl/storage/default/asset_request.hpp> +#include <mbgl/storage/default/http_request.hpp> + +#include <mbgl/storage/response.hpp> + +#include <mbgl/util/async_queue.hpp> +#include <mbgl/util/util.hpp> + +#include <mbgl/util/variant.hpp> + +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#include <boost/algorithm/string.hpp> +#pragma GCC diagnostic pop + +#include <thread> +#include <algorithm> +#include <cassert> + + +namespace algo = boost::algorithm; + +namespace mbgl { + +struct DefaultFileSource::ActionDispatcher { + DefaultFileSource &fileSource; + template <typename T> void operator()(T &t) { fileSource.process(t); } +}; + +struct DefaultFileSource::AddRequestAction { + Request *const request; +}; + +struct DefaultFileSource::RemoveRequestAction { + Request *const request; +}; + +struct DefaultFileSource::ResultAction { + const Resource resource; + std::unique_ptr<Response> response; +}; + +struct DefaultFileSource::StopAction { +}; + + +DefaultFileSource::DefaultFileSource(FileCache *cache_) + : loop(uv_loop_new()), + cache(cache_), + queue(new Queue(loop, [this](Action &action) { + mapbox::util::apply_visitor(ActionDispatcher{*this}, action); + })), + thread([this]() { +#ifdef __APPLE__ + pthread_setname_np("FileSource"); +#endif + uv_run(loop, UV_RUN_DEFAULT); + }) { +} + +DefaultFileSource::DefaultFileSource(FileCache *cache_, uv_loop_t *loop_) + : loop(loop_), + cache(cache_), + queue(new Queue(loop, [this](Action &action) { + mapbox::util::apply_visitor(ActionDispatcher{*this}, action); + })) { + // Make sure that the queue doesn't block the loop from exiting. + queue->unref(); +} + +DefaultFileSource::~DefaultFileSource() { + MBGL_VERIFY_THREAD(tid); + + if (thread.joinable()) { + if (queue) { + queue->send(StopAction{ }); + } + thread.join(); + uv_loop_delete(loop); + } else { + // Assume that the loop we received is running in the current thread. + StopAction action {}; + process(action); + } +} + +SharedRequestBase *DefaultFileSource::find(const Resource &resource) { + // We're using a set of pointers here instead of a map between url and SharedRequestBase because + // we need to find the requests both by pointer and by URL. Given that the number of requests + // is generally very small (typically < 10 at a time), hashing by URL incurs too much overhead + // anyway. + const auto it = pending.find(resource); + if (it != pending.end()) { + return it->second; + } + return nullptr; +} + +Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Callback callback) { + auto req = new Request(resource, l, std::move(callback)); + + // This function can be called from any thread. Make sure we're executing the actual call in the + // file source loop by sending it over the queue. It will be processed in processAction(). + queue->send(AddRequestAction{ req }); + return req; +} + +void DefaultFileSource::request(const Resource &resource, Callback callback) { + auto req = new Request(resource, nullptr, std::move(callback)); + + // This function can be called from any thread. Make sure we're executing the actual call in the + // file source loop by sending it over the queue. It will be processed in processAction(). + queue->send(AddRequestAction{ req }); +} + +void DefaultFileSource::cancel(Request *req) { + req->cancel(); + + // This function can be called from any thread. Make sure we're executing the actual call in the + // file source loop by sending it over the queue. It will be processed in processAction(). + queue->send(RemoveRequestAction{ req }); +} + +void DefaultFileSource::process(AddRequestAction &action) { + const Resource &resource = action.request->resource; + + // We're adding a new Request. + SharedRequestBase *sharedRequest = find(resource); + if (!sharedRequest) { + // There is no request for this URL yet. Create a new one and start it. + if (algo::starts_with(resource.url, "asset://")) { + sharedRequest = new AssetRequest(this, resource); + } else { + sharedRequest = new HTTPRequest(this, resource); + } + + // Make sure the loop stays alive when we're not running the file source in it's own thread. + if (!thread.joinable() && pending.empty()) { + queue->ref(); + } + + const bool inserted = pending.emplace(resource, sharedRequest).second; + assert(inserted); + (void (inserted)); // silence unused variable warning on Release builds. + + // But first, we're going to start querying the database if it exists. + if (!cache) { + sharedRequest->start(loop); + } else { + // Otherwise, first check the cache for existing data so that we can potentially + // revalidate the information without having to redownload everything. + cache->get(resource, [this, resource](std::unique_ptr<Response> response) { + queue->send(ResultAction { resource, std::move(response) }); + }); + } + } + sharedRequest->subscribe(action.request); +} + +void DefaultFileSource::process(RemoveRequestAction &action) { + SharedRequestBase *sharedRequest = find(action.request->resource); + if (sharedRequest) { + // If the number of dependent requests of the SharedRequestBase drops to zero, the + // unsubscribe callback triggers the removal of the SharedRequestBase pointer from the list + // of pending requests and initiates cancelation. + sharedRequest->unsubscribe(action.request); + } else { + // There is no request for this URL anymore. Likely, the request already completed + // before we got around to process the cancelation request. + } + + // Send a message back to the requesting thread and notify it that this request has been + // canceled and is now safe to be deleted. + action.request->destruct(); +} + +void DefaultFileSource::process(ResultAction &action) { + SharedRequestBase *sharedRequest = find(action.resource); + if (sharedRequest) { + if (action.response) { + // This entry was stored in the cache. Now determine if we need to revalidate. + const int64_t now = std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()).count(); + if (action.response->expires > now) { + // The response is fresh. We're good to notify the caller. + sharedRequest->notify(std::move(action.response), FileCache::Hint::No); + sharedRequest->cancel(); + return; + } else { + // The cached response is stale. Now run the real request. + sharedRequest->start(loop, std::move(action.response)); + } + } else { + // There is no response. Now run the real request. + sharedRequest->start(loop); + } + } else { + // There is no request for this URL anymore. Likely, the request was canceled + // before we got around to process the cache result. + } +} + +void DefaultFileSource::process(StopAction &) { + // Cancel all remaining requests. + for (auto it : pending) { + it.second->unsubscribeAll(); + } + pending.clear(); + + assert(queue); + queue->stop(); + queue = nullptr; +} + +void DefaultFileSource::notify(SharedRequestBase *sharedRequest, + const std::set<Request *> &observers, + std::shared_ptr<const Response> response, FileCache::Hint hint) { + // First, remove the request, since it might be destructed at any point now. + assert(find(sharedRequest->resource) == sharedRequest); + pending.erase(sharedRequest->resource); + + if (response) { + if (cache) { + // Store response in database + cache->put(sharedRequest->resource, response, hint); + } + + // Notify all observers. + for (auto it : observers) { + it->notify(response); + } + } + + if (!thread.joinable() && pending.empty()) { + // When there are no pending requests, we're going to allow the queue to stop. + queue->unref(); + } +} + +} diff --git a/src/mbgl/storage/http_request.cpp b/src/mbgl/storage/http_request.cpp deleted file mode 100644 index 57e6c260ef..0000000000 --- a/src/mbgl/storage/http_request.cpp +++ /dev/null @@ -1,280 +0,0 @@ -#include <mbgl/storage/http_request.hpp> -#include <mbgl/storage/sqlite_store.hpp> -#include <mbgl/storage/http_request_baton.hpp> -#include <mbgl/platform/log.hpp> - -#include <uv.h> - -#include <cassert> -#include <chrono> - -namespace mbgl { - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" -#pragma clang diagnostic ignored "-Wexit-time-destructors" -#pragma clang diagnostic ignored "-Wglobal-constructors" - -struct CacheRequestBaton { - HTTPRequest *request = nullptr; - std::string path; - util::ptr<SQLiteStore> store; -}; - -HTTPRequest::HTTPRequest(ResourceType type_, const std::string &path_, uv_loop_t *loop_, util::ptr<SQLiteStore> store_) - : BaseRequest(path_), threadId(std::this_thread::get_id()), loop(loop_), store(store_), type(type_) { - if (store) { - startCacheRequest(); - } else { - startHTTPRequest(nullptr); - } -} - -void HTTPRequest::startCacheRequest() { - assert(std::this_thread::get_id() == threadId); - - cacheBaton = new CacheRequestBaton; - cacheBaton->request = this; - cacheBaton->path = path; - cacheBaton->store = store; - store->get(path, [](std::unique_ptr<Response> &&response_, void *ptr) { - // Wrap in a unique_ptr, so it'll always get auto-destructed. - std::unique_ptr<CacheRequestBaton> baton((CacheRequestBaton *)ptr); - if (baton->request) { - baton->request->cacheBaton = nullptr; - baton->request->handleCacheResponse(std::move(response_)); - } - }, cacheBaton); -} - -void HTTPRequest::handleCacheResponse(std::unique_ptr<Response> &&res) { - assert(std::this_thread::get_id() == threadId); - - if (res) { - // This entry was stored in the cache. Now determine if we need to revalidate. - const int64_t now = std::chrono::duration_cast<std::chrono::seconds>( - std::chrono::system_clock::now().time_since_epoch()).count(); - if (res->expires > now) { - response = std::move(res); - notify(); - // Note: after calling notify(), the request object may cease to exist. - // This HTTPRequest is completed. - return; - } else { - // TODO: notify with preliminary results. - } - } - - startHTTPRequest(std::move(res)); -} - -void HTTPRequest::startHTTPRequest(std::unique_ptr<Response> &&res) { - assert(std::this_thread::get_id() == threadId); - assert(!httpBaton); - - httpBaton = std::make_shared<HTTPRequestBaton>(path); - httpBaton->request = this; - httpBaton->async = new uv_async_t; - httpBaton->response = std::move(res); - httpBaton->async->data = new util::ptr<HTTPRequestBaton>(httpBaton); - -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_async_init(loop, httpBaton->async, [](uv_async_t *async, int) { -#else - uv_async_init(loop, httpBaton->async, [](uv_async_t *async) { -#endif - util::ptr<HTTPRequestBaton> &baton = *(util::ptr<HTTPRequestBaton> *)async->data; - - if (baton->request) { - HTTPRequest *request = baton->request; - request->httpBaton.reset(); - baton->request = nullptr; - request->handleHTTPResponse(baton->type, std::move(baton->response)); - } - - delete (util::ptr<HTTPRequestBaton> *)async->data; - uv_close((uv_handle_t *)async, [](uv_handle_t *handle) { - uv_async_t *async_handle = (uv_async_t *)handle; - delete async_handle; - }); - }); - attempts++; - HTTPRequestBaton::start(httpBaton); -} - - - -void HTTPRequest::handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&res) { - assert(std::this_thread::get_id() == threadId); - assert(!httpBaton); - assert(!response); - - switch (responseType) { - // This error was caused by a temporary error and it is likely that it will be resolved - // immediately. We are going to try again right away. This is like the TemporaryError, - // except that we will not perform exponential back-off. - case HTTPResponseType::SingularError: - if (attempts >= 4) { - // Report as error after 4 attempts. - response = std::move(res); - notify(); - } else if (attempts >= 2) { - // Switch to the back-off algorithm after the second failure. - retryHTTPRequest(std::move(res), (1 << attempts) * 1000); - return; - } else { - startHTTPRequest(std::move(res)); - } - break; - - // This error might be resolved by waiting some time (e.g. server issues). - // We are going to do an exponential back-off and will try again in a few seconds. - case HTTPResponseType::TemporaryError: - if (attempts >= 4) { - // Report error back after it failed completely. - response = std::move(res); - notify(); - } else { - retryHTTPRequest(std::move(res), (1 << attempts) * 1000); - } - break; - - // This error might be resolved once the network reachability status changes. - // We are going to watch the network status for changes and will retry as soon as the - // operating system notifies us of a network status change. - case HTTPResponseType::ConnectionError: - - if (attempts >= 4) { - // Report error back after it failed completely. - response = std::move(res); - notify(); - } else { - // By default, we will retry every 60 seconds. - retryHTTPRequest(std::move(res), 60000); - } - break; - - // The request was canceled programatically. - case HTTPResponseType::Canceled: - response.reset(); - notify(); - break; - - // This error probably won't be resolved by retrying anytime soon. We are giving up. - case HTTPResponseType::PermanentError: - response = std::move(res); - notify(); - break; - - // The request returned data successfully. We retrieved and decoded the data successfully. - case HTTPResponseType::Successful: - if (store) { - store->put(path, type, *res); - } - response = std::move(res); - notify(); - break; - - // The request confirmed that the data wasn't changed. We already have the data. - case HTTPResponseType::NotModified: - if (store) { - store->updateExpiration(path, res->expires); - } - response = std::move(res); - notify(); - break; - - default: - assert(!"Response wasn't set"); - break; - } -} - -using RetryBaton = std::pair<HTTPRequest *, std::unique_ptr<Response>>; - -void HTTPRequest::retryHTTPRequest(std::unique_ptr<Response> &&res, uint64_t timeout) { - assert(std::this_thread::get_id() == threadId); - assert(!backoffTimer); - backoffTimer = new uv_timer_t(); - uv_timer_init(loop, backoffTimer); - backoffTimer->data = new RetryBaton(this, std::move(res)); - -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_timer_start(backoffTimer, [](uv_timer_t *timer, int) { -#else - uv_timer_start(backoffTimer, [](uv_timer_t *timer) { -#endif - std::unique_ptr<RetryBaton> pair { static_cast<RetryBaton *>(timer->data) }; - pair->first->startHTTPRequest(std::move(pair->second)); - pair->first->backoffTimer = nullptr; - uv_timer_stop(timer); - uv_close((uv_handle_t *)timer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; }); - }, timeout, 0); -} - -void HTTPRequest::removeHTTPBaton() { - assert(std::this_thread::get_id() == threadId); - if (httpBaton) { - httpBaton->request = nullptr; - HTTPRequestBaton::stop(httpBaton); - httpBaton.reset(); - } -} - -void HTTPRequest::removeCacheBaton() { - assert(std::this_thread::get_id() == threadId); - if (cacheBaton) { - // Make sre that this object doesn't accidentally get accessed when it is destructed before - // the callback returned. They are being run in the same thread, so just setting it to - // null is sufficient. - // Note: We don't manually delete the CacheRequestBaton since it'll be deleted by the - // callback. - cacheBaton->request = nullptr; - cacheBaton = nullptr; - } -} - -void HTTPRequest::removeBackoffTimer() { - assert(std::this_thread::get_id() == threadId); - if (backoffTimer) { - delete static_cast<RetryBaton *>(backoffTimer->data); - uv_timer_stop(backoffTimer); - uv_close((uv_handle_t *)backoffTimer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; }); - backoffTimer = nullptr; - } -} - -void HTTPRequest::retryImmediately() { - assert(std::this_thread::get_id() == threadId); - if (!cacheBaton && !httpBaton) { - if (backoffTimer) { - // Retry immediately. - uv_timer_stop(backoffTimer); - std::unique_ptr<RetryBaton> pair { static_cast<RetryBaton *>(backoffTimer->data) }; - assert(pair->first == this); - startHTTPRequest(std::move(pair->second)); - uv_close((uv_handle_t *)backoffTimer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; }); - backoffTimer = nullptr; - } else { - assert(!"We should always have a backoffTimer when there are no batons"); - } - } -} - -void HTTPRequest::cancel() { - assert(std::this_thread::get_id() == threadId); - removeCacheBaton(); - removeHTTPBaton(); - removeBackoffTimer(); - notify(); -} - - -HTTPRequest::~HTTPRequest() { - assert(std::this_thread::get_id() == threadId); - cancel(); -} - -#pragma clang diagnostic pop - -} diff --git a/src/mbgl/storage/http_request.hpp b/src/mbgl/storage/http_request.hpp deleted file mode 100644 index 7cc72101d5..0000000000 --- a/src/mbgl/storage/http_request.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef MBGL_STORAGE_HTTP_REQUEST -#define MBGL_STORAGE_HTTP_REQUEST - -#include <mbgl/storage/resource_type.hpp> -#include <mbgl/storage/base_request.hpp> -#include <mbgl/storage/http_request_baton.hpp> - -#include <string> -#include <memory> -#include <cassert> -#include <thread> - -typedef struct uv_loop_s uv_loop_t; -typedef struct uv_timer_s uv_timer_t; - -namespace mbgl { - -struct CacheRequestBaton; -struct HTTPRequestBaton; -struct CacheEntry; -class SQLiteStore; - -class HTTPRequest : public BaseRequest { -public: - HTTPRequest(ResourceType type, const std::string &path, uv_loop_t *loop, util::ptr<SQLiteStore> store); - ~HTTPRequest(); - - void cancel(); - void retryImmediately(); - -private: - void startCacheRequest(); - void handleCacheResponse(std::unique_ptr<Response> &&response); - void startHTTPRequest(std::unique_ptr<Response> &&res); - void handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&response); - - void retryHTTPRequest(std::unique_ptr<Response> &&res, uint64_t timeout); - - void removeCacheBaton(); - void removeHTTPBaton(); - void removeBackoffTimer(); - -private: - const std::thread::id threadId; - uv_loop_t *const loop; - CacheRequestBaton *cacheBaton = nullptr; - util::ptr<HTTPRequestBaton> httpBaton; - uv_timer_t *backoffTimer = nullptr; - util::ptr<SQLiteStore> store; - const ResourceType type; - uint8_t attempts = 0; - - friend struct HTTPRequestBaton; -}; - -} - -#endif diff --git a/src/mbgl/storage/http_request_baton.cpp b/src/mbgl/storage/http_request_baton.cpp deleted file mode 100644 index d781a3bdf4..0000000000 --- a/src/mbgl/storage/http_request_baton.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include <mbgl/storage/http_request_baton.hpp> -#include <uv.h> - -namespace mbgl { - -HTTPRequestBaton::HTTPRequestBaton(const std::string &path_) : threadId(std::this_thread::get_id()), path(path_) { -} - -HTTPRequestBaton::~HTTPRequestBaton() { -} - -} diff --git a/src/mbgl/storage/network_status.cpp b/src/mbgl/storage/network_status.cpp new file mode 100644 index 0000000000..04b6937d94 --- /dev/null +++ b/src/mbgl/storage/network_status.cpp @@ -0,0 +1,32 @@ +#include <mbgl/storage/network_status.hpp> + +#include <uv.h> + +// Example: Allocate a reachability object +// Reachability* reach = [Reachability reachabilityForInternetConnection]; +// reach.reachableBlock = ^(Reachability* reach) { NetworkStatus::Reachable(); }; +// [reach startNotifier]; + +namespace mbgl { + +std::mutex NetworkStatus::mtx; +std::set<uv_async_t *> NetworkStatus::observers; + +void NetworkStatus::Subscribe(uv_async_t *async) { + std::lock_guard<std::mutex> lock(NetworkStatus::mtx); + observers.insert(async); +} + +void NetworkStatus::Unsubscribe(uv_async_t *async) { + std::lock_guard<std::mutex> lock(NetworkStatus::mtx); + observers.erase(async); +} + +void NetworkStatus::Reachable() { + std::lock_guard<std::mutex> lock(NetworkStatus::mtx); + for (auto async : observers) { + uv_async_send(async); + } +} + +} diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp index 39fbd36789..c1a57e7256 100644 --- a/src/mbgl/storage/request.cpp +++ b/src/mbgl/storage/request.cpp @@ -1,49 +1,99 @@ -#include <mbgl/storage/request.hpp> -#include <mbgl/storage/base_request.hpp> +#include <mbgl/storage/default/request.hpp> + +#include <mbgl/storage/response.hpp> + +#include <mbgl/util/util.hpp> +#include <mbgl/util/uv.hpp> #include <uv.h> #include <cassert> +#include <functional> namespace mbgl { -Request::Request(const util::ptr<BaseRequest> &base_) - : thread_id(std::this_thread::get_id()), base(base_) { +// Note: This requires that loop is running in the current thread (or not yet running). +Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_) + : callback(callback_), resource(resource_) { + // When there is no loop supplied (== nullptr), the callback will be fired in an arbitrary + // thread (the thread notify() is called from) rather than kicking back to the calling thread. + if (loop) { + notify_async = new uv_async_t; + notify_async->data = this; +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + uv_async_init(loop, notify_async, [](uv_async_t *async, int) { notifyCallback(async); }); +#else + uv_async_init(loop, notify_async, notifyCallback); +#endif + } } -Request::~Request() { - assert(thread_id == std::this_thread::get_id()); +void Request::notifyCallback(uv_async_t *async) { + auto request = reinterpret_cast<Request *>(async->data); + uv::close(async); + + if (!request->destruct_async) { + // We haven't created a cancel request, so we can safely delete this Request object + // since it won't be accessed in the future. + assert(request->response); + request->callback(*request->response); + delete request; + } else { + // Otherwise, we're waiting for for the destruct notification to be delivered in order + // to delete the Request object. We're doing this since we can't know whether the + // DefaultFileSource is still sending a cancel event, which means this object must still + // exist. + } } -void Request::onload(CompletedCallback cb) { - assert(thread_id == std::this_thread::get_id()); - if (base) { - Callback *callback = base->add(std::move(cb), base); - if (callback) { - callbacks.push_front(callback); - } + +Request::~Request() { + if (notify_async) { + // Request objects can be destructed in other threads when the user didn't supply a loop. + MBGL_VERIFY_THREAD(tid) } } -void Request::oncancel(AbortedCallback cb) { - assert(thread_id == std::this_thread::get_id()); - if (base) { - Callback *callback = base->add(std::move(cb), base); - if (callback) { - callbacks.push_front(callback); - } +void Request::notify(const std::shared_ptr<const Response> &response_) { + response = response_; + if (notify_async) { + uv_async_send(notify_async); + } else { + assert(response); + callback(*response); + delete this; } } void Request::cancel() { - assert(thread_id == std::this_thread::get_id()); - if (base) { - for (Callback *callback : callbacks) { - base->remove(callback); - } - base.reset(); + MBGL_VERIFY_THREAD(tid) + assert(notify_async); + assert(!destruct_async); + destruct_async = new uv_async_t; + destruct_async->data = this; +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + uv_async_init(notify_async->loop, destruct_async, [](uv_async_t *async, int) { cancelCallback(async); }); +#else + uv_async_init(notify_async->loop, destruct_async, cancelCallback); +#endif +} + +void Request::cancelCallback(uv_async_t *async) { + // The destruct_async will be invoked *after* the notify_async callback has already run. + auto request = reinterpret_cast<Request *>(async->data); + uv::close(async); + delete request; +} + +// This gets called from the FileSource thread, and will only ever be invoked after cancel() was called +// in the original requesting thread. +void Request::destruct() { + if (notify_async) { + notify(nullptr); } - callbacks.clear(); + + assert(destruct_async); + uv_async_send(destruct_async); } } diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp deleted file mode 100644 index a08a6d31ce..0000000000 --- a/src/mbgl/storage/response.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include <mbgl/storage/response.hpp> - -#include <chrono> - -namespace mbgl { - -int64_t Response::parseCacheControl(const char *value) { - if (value) { - unsigned long long seconds = 0; - // TODO: cache-control may contain other information as well: - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 - if (std::sscanf(value, "max-age=%llu", &seconds) == 1) { - return std::chrono::duration_cast<std::chrono::seconds>( - std::chrono::system_clock::now().time_since_epoch()).count() + - seconds; - } - } - - return -1; -} - -} diff --git a/src/mbgl/storage/sqlite_store.cpp b/src/mbgl/storage/sqlite_store.cpp deleted file mode 100644 index d382921dec..0000000000 --- a/src/mbgl/storage/sqlite_store.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include <mbgl/storage/sqlite_store.hpp> -#include <mbgl/util/compression.hpp> -#include <mbgl/util/sqlite3.hpp> -#include <mbgl/util/std.hpp> - -#include <mbgl/util/uv-worker.h> - -#include <cassert> - -using namespace mapbox::sqlite; - -std::string removeAccessTokenFromURL(const std::string &url) { - const size_t token_start = url.find("access_token="); - // Ensure that token exists, isn't at the front and is preceded by either & or ?. - if (token_start == std::string::npos || token_start == 0 || !(url[token_start - 1] == '&' || url[token_start - 1] == '?')) { - return url; - } - - const size_t token_end = url.find_first_of('&', token_start); - if (token_end == std::string::npos) { - // The token is the last query argument. We slice away the "&access_token=..." part - return url.substr(0, token_start - 1); - } else { - // We slice away the "access_token=...&" part. - return url.substr(0, token_start) + url.substr(token_end + 1); - } -} - -std::string convertMapboxDomainsToProtocol(const std::string &url) { - const size_t protocol_separator = url.find("://"); - if (protocol_separator == std::string::npos) { - return url; - } - - const std::string protocol = url.substr(0, protocol_separator); - if (!(protocol == "http" || protocol == "https")) { - return url; - } - - const size_t domain_begin = protocol_separator + 3; - const size_t path_separator = url.find("/", domain_begin); - if (path_separator == std::string::npos) { - return url; - } - - const std::string domain = url.substr(domain_begin, path_separator - domain_begin); - if (domain.find(".tiles.mapbox.com") != std::string::npos) { - return "mapbox://" + url.substr(path_separator + 1); - } else { - return url; - } -} - -std::string unifyMapboxURLs(const std::string &url) { - return removeAccessTokenFromURL(convertMapboxDomainsToProtocol(url)); -} - -namespace mbgl { - -SQLiteStore::SQLiteStore(uv_loop_t *loop, const std::string &path) - : thread_id(std::this_thread::get_id()), - db(std::make_shared<Database>(path.c_str(), ReadWrite | Create)) { - createSchema(); - worker = new uv_worker_t; - uv_worker_init(worker, loop, 1, "SQLite"); -} - -SQLiteStore::~SQLiteStore() { - // Nothing to do. This function needs to be here because we're forward-declaring - // Database, so we need the actual definition here to be able to properly destruct it. - if (worker) { - uv_worker_close(worker, [](uv_worker_t *worker_handle) { - delete worker_handle; - }); - } -} - -void SQLiteStore::createSchema() { - if (!db || !*db) { - return; - } - - db->exec("CREATE TABLE IF NOT EXISTS `http_cache` (" - " `url` TEXT PRIMARY KEY NOT NULL," - " `code` INTEGER NOT NULL," - " `type` INTEGER NOT NULL," - " `modified` INTEGER," - " `etag` TEXT," - " `expires` INTEGER," - " `data` BLOB," - " `compressed` INTEGER NOT NULL DEFAULT 0" - ");" - "CREATE INDEX IF NOT EXISTS `http_cache_type_idx` ON `http_cache` (`type`);"); -} - -struct GetBaton { - util::ptr<Database> db; - std::string path; - ResourceType type; - void *ptr = nullptr; - SQLiteStore::GetCallback callback = nullptr; - std::unique_ptr<Response> response; -}; - -void SQLiteStore::get(const std::string &path, GetCallback callback, void *ptr) { - assert(std::this_thread::get_id() == thread_id); - if (!db || !*db) { - if (callback) { - callback(nullptr, ptr); - } - return; - } - - GetBaton *get_baton = new GetBaton; - get_baton->db = db; - get_baton->path = path; - get_baton->ptr = ptr; - get_baton->callback = callback; - - uv_worker_send(worker, get_baton, [](void *data) { - GetBaton *baton = (GetBaton *)data; - const std::string url = unifyMapboxURLs(baton->path); - // 0 1 2 - Statement stmt = baton->db->prepare("SELECT `code`, `type`, `modified`, " - // 3 4 5 6 - "`etag`, `expires`, `data`, `compressed` FROM `http_cache` WHERE `url` = ?"); - - stmt.bind(1, url.c_str()); - if (stmt.run()) { - // There is data. - baton->response = util::make_unique<Response>(); - - baton->response->code = stmt.get<int>(0); - baton->type = ResourceType(stmt.get<int>(1)); - baton->response->modified = stmt.get<int64_t>(2); - baton->response->etag = stmt.get<std::string>(3); - baton->response->expires = stmt.get<int64_t>(4); - baton->response->data = stmt.get<std::string>(5); - if (stmt.get<int>(6)) { // == compressed - baton->response->data = util::decompress(baton->response->data); - } - } else { - // There is no data. - // This is a noop. - } - }, [](void *data) { - std::unique_ptr<GetBaton> baton { (GetBaton *)data }; - if (baton->callback) { - baton->callback(std::move(baton->response), baton->ptr); - } - }); -} - - -struct PutBaton { - util::ptr<Database> db; - std::string path; - ResourceType type; - Response response; -}; - -void SQLiteStore::put(const std::string &path, ResourceType type, const Response &response) { - assert(std::this_thread::get_id() == thread_id); - if (!db) return; - - PutBaton *put_baton = new PutBaton; - put_baton->db = db; - put_baton->path = path; - put_baton->type = type; - put_baton->response = response; - - uv_worker_send(worker, put_baton, [](void *data) { - PutBaton *baton = (PutBaton *)data; - const std::string url = unifyMapboxURLs(baton->path); - Statement stmt = baton->db->prepare("REPLACE INTO `http_cache` (" - // 1 2 3 4 5 6 7 8 - "`url`, `code`, `type`, `modified`, `etag`, `expires`, `data`, `compressed`" - ") VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); - stmt.bind(1, url.c_str()); - stmt.bind(2, int(baton->response.code)); - stmt.bind(3, int(baton->type)); - stmt.bind(4, baton->response.modified); - stmt.bind(5, baton->response.etag.c_str()); - stmt.bind(6, baton->response.expires); - - if (baton->type == ResourceType::Image) { - stmt.bind(7, baton->response.data, false); // do not retain the string internally. - stmt.bind(8, false); - } else { - stmt.bind(7, util::compress(baton->response.data), true); // retain the string internally. - stmt.bind(8, true); - } - - stmt.run(); - }, [](void *data) { - delete (PutBaton *)data; - }); -} - -struct ExpirationBaton { - util::ptr<Database> db; - std::string path; - int64_t expires; -}; - -void SQLiteStore::updateExpiration(const std::string &path, int64_t expires) { - assert(std::this_thread::get_id() == thread_id); - if (!db || !*db) return; - - ExpirationBaton *expiration_baton = new ExpirationBaton; - expiration_baton->db = db; - expiration_baton->path = path; - expiration_baton->expires = expires; - - uv_worker_send(worker, expiration_baton, [](void *data) { - ExpirationBaton *baton = (ExpirationBaton *)data; - const std::string url = unifyMapboxURLs(baton->path); - Statement stmt = // 1 2 - baton->db->prepare("UPDATE `http_cache` SET `expires` = ? WHERE `url` = ?"); - stmt.bind<int64_t>(1, baton->expires); - stmt.bind(2, url.c_str()); - stmt.run(); - }, [](void *data) { - delete (ExpirationBaton *)data; - }); -} - -} diff --git a/src/mbgl/storage/sqlite_store.hpp b/src/mbgl/storage/sqlite_store.hpp deleted file mode 100644 index 988eca2597..0000000000 --- a/src/mbgl/storage/sqlite_store.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef MBGL_STORAGE_SQLITE_STORE -#define MBGL_STORAGE_SQLITE_STORE - -#include <mbgl/storage/response.hpp> -#include <mbgl/storage/resource_type.hpp> -#include <mbgl/util/ptr.hpp> - -#include <uv.h> - -#include <string> -#include <thread> - -typedef struct uv_worker_s uv_worker_t; - -namespace mapbox { -namespace sqlite { -class Database; -} -} - -namespace mbgl { - -class SQLiteStore { -public: - SQLiteStore(uv_loop_t *loop, const std::string &path); - ~SQLiteStore(); - - typedef void (*GetCallback)(std::unique_ptr<Response> &&entry, void *ptr); - - void get(const std::string &path, GetCallback cb, void *ptr); - void put(const std::string &path, ResourceType type, const Response &entry); - void updateExpiration(const std::string &path, int64_t expires); - -private: - void createSchema(); - void closeDatabase(); - static void runGet(uv_work_t *req); - static void runPut(uv_work_t *req); - static void deliverResult(uv_work_t *req, int status); - -private: - const std::thread::id thread_id; - util::ptr<mapbox::sqlite::Database> db; - uv_worker_t *worker = nullptr; -}; - -} - -#endif diff --git a/src/mbgl/style/applied_class_properties.cpp b/src/mbgl/style/applied_class_properties.cpp index 9037c6ad5d..6e304e69d1 100644 --- a/src/mbgl/style/applied_class_properties.cpp +++ b/src/mbgl/style/applied_class_properties.cpp @@ -2,18 +2,18 @@ namespace mbgl { -AppliedClassProperty::AppliedClassProperty(ClassID class_id, timestamp begin_, timestamp end_, const PropertyValue &value_) +AppliedClassProperty::AppliedClassProperty(ClassID class_id, std::chrono::steady_clock::time_point begin_, std::chrono::steady_clock::time_point end_, const PropertyValue &value_) : name(class_id), begin(begin_), end(end_), value(value_) {} -// Returns thie ID of the most recent +// Returns the ID of the most recent ClassID AppliedClassProperties::mostRecent() const { return properties.size() ? properties.back().name : ClassID::Fallback; } -void AppliedClassProperties::add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value) { +void AppliedClassProperties::add(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value) { properties.emplace_back(class_id, begin, end, value); } @@ -23,7 +23,7 @@ bool AppliedClassProperties::hasTransitions() const { // Erase all items in the property list that are before a completed transition. // Then, if the only remaining property is a Fallback value, remove it too. -void AppliedClassProperties::cleanup(timestamp now) { +void AppliedClassProperties::cleanup(std::chrono::steady_clock::time_point now) { // Iterate backwards, but without using the rbegin/rend interface since we need forward // iterators to use .erase(). for (auto it = properties.end(), begin = properties.begin(); it != begin;) { @@ -49,4 +49,4 @@ bool AppliedClassProperties::empty() const { return properties.empty(); } -}
\ No newline at end of file +} diff --git a/src/mbgl/style/applied_class_properties.hpp b/src/mbgl/style/applied_class_properties.hpp index 827f15a2a1..bd84095590 100644 --- a/src/mbgl/style/applied_class_properties.hpp +++ b/src/mbgl/style/applied_class_properties.hpp @@ -3,20 +3,20 @@ #include <mbgl/style/property_value.hpp> #include <mbgl/style/class_dictionary.hpp> -#include <mbgl/util/time.hpp> #include <list> +#include <chrono> namespace mbgl { class AppliedClassProperty { public: - AppliedClassProperty(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value); + AppliedClassProperty(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value); public: const ClassID name; - const timestamp begin; - const timestamp end; + const std::chrono::steady_clock::time_point begin; + const std::chrono::steady_clock::time_point end; const PropertyValue value; }; @@ -26,11 +26,11 @@ public: std::list<AppliedClassProperty> properties; public: - // Returns thie ID of the most recent + // Returns the ID of the most recent ClassID mostRecent() const; - void add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value); + void add(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value); bool hasTransitions() const; - void cleanup(timestamp now); + void cleanup(std::chrono::steady_clock::time_point now); bool empty() const; }; diff --git a/src/mbgl/style/fadedfunction_properties.cpp b/src/mbgl/style/fadedfunction_properties.cpp index 8ac4f3be42..21c3267519 100644 --- a/src/mbgl/style/fadedfunction_properties.cpp +++ b/src/mbgl/style/fadedfunction_properties.cpp @@ -20,9 +20,7 @@ T FadedStopsFunction<T>::evaluate(float z, const ZoomHistory &zh) const { T result; float fraction = std::fmod(z, 1.0f); - float tDiff = (util::now() - zh.lastIntegerZoomTime) / 1_millisecond; - float fDuration = duration / 1_millisecond; - float t = std::min(tDiff / fDuration, 1.0f); + float t = std::min((std::chrono::steady_clock::now() - zh.lastIntegerZoomTime) / duration, 1.0f); float scale = 1.0f; uint32_t low, high; diff --git a/src/mbgl/style/fadedfunction_properties.hpp b/src/mbgl/style/fadedfunction_properties.hpp index 23b4411369..7313d32759 100644 --- a/src/mbgl/style/fadedfunction_properties.hpp +++ b/src/mbgl/style/fadedfunction_properties.hpp @@ -9,14 +9,14 @@ namespace mbgl { template <typename T> struct FadedStopsFunction { - inline FadedStopsFunction(const std::vector<std::pair<float, T>> &values_, timestamp duration_) : values(values_), duration(duration_) {} - inline FadedStopsFunction(T &value) : values({{ 0, value }}), duration(300_milliseconds) {} - inline FadedStopsFunction() : values(), duration(300_milliseconds) {} + inline FadedStopsFunction(const std::vector<std::pair<float, T>> &values_, std::chrono::duration<float> duration_) : values(values_), duration(duration_) {} + inline FadedStopsFunction(T &value) : values({{ 0, value }}), duration(300) {} + inline FadedStopsFunction() : values(), duration(300) {} T evaluate(float z, const ZoomHistory &zoomHistory) const; private: const std::vector<std::pair<float, T>> values; - const timestamp duration; + const std::chrono::duration<float> duration; }; } diff --git a/src/mbgl/style/property_fallback.cpp b/src/mbgl/style/property_fallback.cpp index dc747b37e6..5fc3ce1f04 100644 --- a/src/mbgl/style/property_fallback.cpp +++ b/src/mbgl/style/property_fallback.cpp @@ -1,5 +1,6 @@ #include <mbgl/style/property_fallback.hpp> #include <mbgl/style/style_properties.hpp> +#include <mbgl/style/style_bucket.hpp> namespace mbgl { @@ -52,6 +53,48 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = { { PropertyKey::BackgroundOpacity, defaultStyleProperties<BackgroundProperties>().opacity }, { PropertyKey::BackgroundColor, defaultStyleProperties<BackgroundProperties>().color }, + + { PropertyKey::LineCap, defaultLayoutProperties<StyleBucketLine>().cap }, + { PropertyKey::LineJoin, defaultLayoutProperties<StyleBucketLine>().join }, + { PropertyKey::LineMiterLimit, defaultLayoutProperties<StyleBucketLine>().miter_limit }, + { PropertyKey::LineRoundLimit, defaultLayoutProperties<StyleBucketLine>().round_limit }, + + { PropertyKey::SymbolPlacement, defaultLayoutProperties<StyleBucketSymbol>().placement }, + { PropertyKey::SymbolMinDistance, defaultLayoutProperties<StyleBucketSymbol>().min_distance }, + { PropertyKey::SymbolAvoidEdges, defaultLayoutProperties<StyleBucketSymbol>().avoid_edges }, + + { PropertyKey::IconAllowOverlap, defaultLayoutProperties<StyleBucketSymbol>().icon.allow_overlap }, + { PropertyKey::IconIgnorePlacement, defaultLayoutProperties<StyleBucketSymbol>().icon.ignore_placement }, + { PropertyKey::IconOptional, defaultLayoutProperties<StyleBucketSymbol>().icon.optional }, + { PropertyKey::IconRotationAlignment, defaultLayoutProperties<StyleBucketSymbol>().icon.rotation_alignment }, + { PropertyKey::IconMaxSize, defaultLayoutProperties<StyleBucketSymbol>().icon.max_size }, + { PropertyKey::IconImage, defaultLayoutProperties<StyleBucketSymbol>().icon.image }, + { PropertyKey::IconRotate, defaultLayoutProperties<StyleBucketSymbol>().icon.rotate }, + { PropertyKey::IconPadding, defaultLayoutProperties<StyleBucketSymbol>().icon.padding }, + { PropertyKey::IconKeepUpright, defaultLayoutProperties<StyleBucketSymbol>().icon.keep_upright }, + { PropertyKey::IconOffsetX, defaultLayoutProperties<StyleBucketSymbol>().icon.offset[0] }, + { PropertyKey::IconOffsetY, defaultLayoutProperties<StyleBucketSymbol>().icon.offset[1] }, + + { PropertyKey::TextRotationAlignment, defaultLayoutProperties<StyleBucketSymbol>().text.rotation_alignment }, + { PropertyKey::TextField, defaultLayoutProperties<StyleBucketSymbol>().text.field }, + { PropertyKey::TextFont, defaultLayoutProperties<StyleBucketSymbol>().text.font }, + { PropertyKey::TextMaxSize, defaultLayoutProperties<StyleBucketSymbol>().text.max_size }, + { PropertyKey::TextMaxWidth, defaultLayoutProperties<StyleBucketSymbol>().text.max_width }, + { PropertyKey::TextLineHeight, defaultLayoutProperties<StyleBucketSymbol>().text.line_height }, + { PropertyKey::TextLetterSpacing, defaultLayoutProperties<StyleBucketSymbol>().text.letter_spacing }, + { PropertyKey::TextJustify, defaultLayoutProperties<StyleBucketSymbol>().text.justify }, + { PropertyKey::TextAnchor, defaultLayoutProperties<StyleBucketSymbol>().text.anchor }, + { PropertyKey::TextMaxAngle, defaultLayoutProperties<StyleBucketSymbol>().text.max_angle }, + { PropertyKey::TextRotate, defaultLayoutProperties<StyleBucketSymbol>().text.rotate }, + { PropertyKey::TextPadding, defaultLayoutProperties<StyleBucketSymbol>().text.padding }, + { PropertyKey::TextKeepUpright, defaultLayoutProperties<StyleBucketSymbol>().text.keep_upright }, + { PropertyKey::TextTransform, defaultLayoutProperties<StyleBucketSymbol>().text.transform }, + { PropertyKey::TextOffsetX, defaultLayoutProperties<StyleBucketSymbol>().text.offset[0] }, + { PropertyKey::TextOffsetY, defaultLayoutProperties<StyleBucketSymbol>().text.offset[1] }, + { PropertyKey::TextAllowOverlap, defaultLayoutProperties<StyleBucketSymbol>().text.allow_overlap }, + { PropertyKey::TextIgnorePlacement, defaultLayoutProperties<StyleBucketSymbol>().text.ignore_placement }, + { PropertyKey::TextOptional, defaultLayoutProperties<StyleBucketSymbol>().text.optional }, + }; const PropertyValue PropertyFallbackValue::defaultProperty = false; diff --git a/src/mbgl/style/property_key.hpp b/src/mbgl/style/property_key.hpp index e283f11579..f10607a7af 100644 --- a/src/mbgl/style/property_key.hpp +++ b/src/mbgl/style/property_key.hpp @@ -26,8 +26,16 @@ enum class PropertyKey { LineDashArray, // for transitions only LineImage, + LineCap, + LineJoin, + LineMiterLimit, + LineRoundLimit, + + SymbolPlacement, + SymbolMinDistance, + SymbolAvoidEdges, + IconOpacity, - IconRotate, IconSize, IconColor, IconHaloColor, @@ -38,6 +46,18 @@ enum class PropertyKey { IconTranslateY, IconTranslateAnchor, + IconAllowOverlap, + IconIgnorePlacement, + IconOptional, + IconRotationAlignment, + IconMaxSize, + IconImage, + IconPadding, + IconRotate, + IconKeepUpright, + IconOffsetX, + IconOffsetY, + TextOpacity, TextSize, TextColor, @@ -49,6 +69,26 @@ enum class PropertyKey { TextTranslateY, TextTranslateAnchor, + TextRotationAlignment, + TextField, + TextFont, + TextMaxSize, + TextMaxWidth, + TextLineHeight, + TextLetterSpacing, + TextMaxAngle, + TextRotate, + TextPadding, + TextIgnorePlacement, + TextOptional, + TextJustify, + TextAnchor, + TextKeepUpright, + TextTransform, + TextOffsetX, + TextOffsetY, + TextAllowOverlap, + RasterOpacity, RasterHueRotate, RasterBrightness, // for transitions only @@ -60,7 +100,9 @@ enum class PropertyKey { BackgroundOpacity, BackgroundColor, - BackgroundImage + BackgroundImage, + + Visibilty }; } diff --git a/src/mbgl/style/property_transition.hpp b/src/mbgl/style/property_transition.hpp index 07b7cfe288..cf9167cc0f 100644 --- a/src/mbgl/style/property_transition.hpp +++ b/src/mbgl/style/property_transition.hpp @@ -2,14 +2,15 @@ #define MBGL_STYLE_PROPERTY_TRANSITION #include <cstdint> +#include <chrono> namespace mbgl { struct PropertyTransition { - uint16_t duration = 0; - uint16_t delay = 0; + std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero(); + std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero(); }; } -#endif
\ No newline at end of file +#endif diff --git a/src/mbgl/style/property_value.hpp b/src/mbgl/style/property_value.hpp index 8dcb8d7371..49e837cf12 100644 --- a/src/mbgl/style/property_value.hpp +++ b/src/mbgl/style/property_value.hpp @@ -14,6 +14,15 @@ typedef mapbox::util::variant< std::string, TranslateAnchorType, RotateAnchorType, + bool, + CapType, + JoinType, + VisibilityType, + PlacementType, + RotationAlignmentType, + TextTransformType, + TextJustifyType, + TextAnchorType, Function<bool>, Function<float>, Function<Color>, diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index dc764106ce..45217950f6 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -4,7 +4,6 @@ #include <mbgl/style/style_parser.hpp> #include <mbgl/style/style_bucket.hpp> #include <mbgl/util/constants.hpp> -#include <mbgl/util/time.hpp> #include <mbgl/util/error.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/uv_detail.hpp> @@ -26,7 +25,7 @@ Style::Style() // for deleting the std::unique_ptr<uv::rwlock>. Style::~Style() {} -void Style::updateProperties(float z, timestamp now) { +void Style::updateProperties(float z, std::chrono::steady_clock::time_point now) { uv::writelock lock(mtx); zoomHistory.update(z, now); @@ -46,13 +45,13 @@ const std::string &Style::getSpriteURL() const { return sprite_url; } -void Style::setDefaultTransitionDuration(uint16_t duration_milliseconds) { - defaultTransition.duration = duration_milliseconds; +void Style::setDefaultTransitionDuration(std::chrono::steady_clock::duration duration) { + defaultTransition.duration = duration; } void Style::cascadeClasses(const std::vector<std::string>& classes) { if (layers) { - layers->setClasses(classes, util::now(), defaultTransition); + layers->setClasses(classes, std::chrono::steady_clock::now(), defaultTransition); } } diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index 434b394217..4de827a38c 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -5,7 +5,6 @@ #include <mbgl/style/style_source.hpp> #include <mbgl/style/zoom_history.hpp> -#include <mbgl/util/time.hpp> #include <mbgl/util/uv.hpp> #include <mbgl/util/ptr.hpp> @@ -15,6 +14,7 @@ #include <unordered_map> #include <vector> #include <set> +#include <chrono> namespace mbgl { @@ -22,7 +22,7 @@ class Sprite; class StyleLayer; class StyleLayerGroup; -class Style { +class Style : public util::noncopyable { public: struct exception : std::runtime_error { exception(const char *msg) : std::runtime_error(msg) {} }; @@ -32,9 +32,9 @@ public: void loadJSON(const uint8_t *const data); size_t layerCount() const; - void updateProperties(float z, timestamp t); + void updateProperties(float z, std::chrono::steady_clock::time_point now); - void setDefaultTransitionDuration(uint16_t duration_milliseconds = 0); + void setDefaultTransitionDuration(std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void cascadeClasses(const std::vector<std::string>&); bool hasTransitions() const; @@ -44,6 +44,7 @@ public: util::ptr<StyleLayerGroup> layers; std::vector<std::string> appliedClasses; std::string glyph_url; + std::string base; private: std::string sprite_url; diff --git a/src/mbgl/style/style_bucket.cpp b/src/mbgl/style/style_bucket.cpp index 9a40c2386b..6e866f3035 100644 --- a/src/mbgl/style/style_bucket.cpp +++ b/src/mbgl/style/style_bucket.cpp @@ -2,12 +2,18 @@ namespace mbgl { -StyleBucket::StyleBucket(StyleLayerType type) { +template<> const StyleBucketFill &defaultLayoutProperties() { static StyleBucketFill p; return p; } +template<> const StyleBucketLine &defaultLayoutProperties() { static StyleBucketLine p; return p; } +template<> const StyleBucketSymbol &defaultLayoutProperties() { static StyleBucketSymbol p; return p; } +template<> const StyleBucketRaster &defaultLayoutProperties() { static StyleBucketRaster p; return p; } +template<> const StyleBucketBackground &defaultLayoutProperties() { static StyleBucketBackground p; return p; } + +StyleBucket::StyleBucket(StyleLayerType type_) : type(type_) { switch (type) { - case StyleLayerType::Fill: render = StyleBucketFill{}; break; - case StyleLayerType::Line: render = StyleBucketLine{}; break; - case StyleLayerType::Symbol: render = StyleBucketSymbol{}; break; - case StyleLayerType::Raster: render = StyleBucketRaster{}; break; + case StyleLayerType::Fill: render.set<StyleBucketFill>(); break; + case StyleLayerType::Line: render.set<StyleBucketLine>(); break; + case StyleLayerType::Symbol: render.set<StyleBucketSymbol>(); break; + case StyleLayerType::Raster: render.set<StyleBucketRaster>(); break; default: break; } } diff --git a/src/mbgl/style/style_bucket.hpp b/src/mbgl/style/style_bucket.hpp index 7a70e32c34..eac899cb97 100644 --- a/src/mbgl/style/style_bucket.hpp +++ b/src/mbgl/style/style_bucket.hpp @@ -9,6 +9,7 @@ #include <mbgl/util/variant.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/ptr.hpp> +#include <mbgl/style/class_properties.hpp> #include <forward_list> @@ -18,11 +19,23 @@ class Source; class StyleBucketFill { public: - WindingType winding = WindingType::NonZero; + // Make movable only. + StyleBucketFill() = default; + StyleBucketFill(StyleBucketFill &&) = default; + StyleBucketFill& operator=(StyleBucketFill &&) = default; + StyleBucketFill(const StyleBucketFill &) = delete; + StyleBucketFill& operator=(const StyleBucketFill &) = delete; }; class StyleBucketLine { public: + // Make movable only. + StyleBucketLine() = default; + StyleBucketLine(StyleBucketLine &&) = default; + StyleBucketLine& operator=(StyleBucketLine &&) = default; + StyleBucketLine(const StyleBucketLine &) = delete; + StyleBucketLine& operator=(const StyleBucketLine &) = delete; + CapType cap = CapType::Butt; JoinType join = JoinType::Miter; float miter_limit = 2.0f; @@ -32,11 +45,11 @@ public: class StyleBucketSymbol { public: // Make movable only. - inline StyleBucketSymbol() = default; - inline StyleBucketSymbol(StyleBucketSymbol &&) = default; - inline StyleBucketSymbol& operator=(StyleBucketSymbol &&) = default; - inline StyleBucketSymbol(const StyleBucketSymbol &) = delete; - inline StyleBucketSymbol& operator=(const StyleBucketSymbol &) = delete; + StyleBucketSymbol() = default; + StyleBucketSymbol(StyleBucketSymbol &&) = default; + StyleBucketSymbol& operator=(StyleBucketSymbol &&) = default; + StyleBucketSymbol(const StyleBucketSymbol &) = delete; + StyleBucketSymbol& operator=(const StyleBucketSymbol &) = delete; PlacementType placement = PlacementType::Point; float min_distance = 250.0f; @@ -52,7 +65,7 @@ public: float rotate = 0.0f; float padding = 2.0f; bool keep_upright = false; - vec2<float> offset = {0, 0}; + std::array<float, 2> offset = {{ 0, 0 }}; } icon; struct { @@ -60,18 +73,17 @@ public: std::string field; std::string font; float max_size = 16.0f; - float max_width = 15.0f * 24 /* em */; - float line_height = 1.2f * 24 /* em */; - float letter_spacing = 0.0f * 24 /* em */; + float max_width = 15.0f /* em */; + float line_height = 1.2f /* em */; + float letter_spacing = 0.0f /* em */; TextJustifyType justify = TextJustifyType::Center; TextAnchorType anchor = TextAnchorType::Center; float max_angle = 45.0f /* degrees */; float rotate = 0.0f; - float slant = 0.0f; float padding = 2.0f; bool keep_upright = true; TextTransformType transform = TextTransformType::None; - vec2<float> offset = {0, 0}; + std::array<float, 2> offset = {{ 0, 0 }}; bool allow_overlap = false; bool ignore_placement = false; bool optional = false; @@ -80,29 +92,41 @@ public: class StyleBucketRaster { public: + // Make movable only. + StyleBucketRaster() = default; + StyleBucketRaster(StyleBucketRaster &&) = default; + StyleBucketRaster& operator=(StyleBucketRaster &&) = default; + StyleBucketRaster(const StyleBucketRaster &) = delete; + StyleBucketRaster& operator=(const StyleBucketRaster &) = delete; +}; + +class StyleBucketBackground { +public: }; typedef mapbox::util::variant<StyleBucketFill, StyleBucketLine, StyleBucketSymbol, - StyleBucketRaster, std::false_type> StyleBucketRender; + StyleBucketRaster, StyleBucketBackground, std::false_type> StyleBucketRender; -class StyleBucket { +class StyleBucket : public util::noncopyable { public: typedef util::ptr<StyleBucket> Ptr; StyleBucket(StyleLayerType type); - + StyleLayerType type; std::string name; util::ptr<StyleSource> style_source; std::string source_layer; FilterExpression filter; + ClassProperties layout; StyleBucketRender render = std::false_type(); float min_zoom = -std::numeric_limits<float>::infinity(); float max_zoom = std::numeric_limits<float>::infinity(); VisibilityType visibility = VisibilityType::Visible; }; - +template <typename T> +const T &defaultLayoutProperties(); }; diff --git a/src/mbgl/style/style_layer.cpp b/src/mbgl/style/style_layer.cpp index bb9688e574..795c651b85 100644 --- a/src/mbgl/style/style_layer.cpp +++ b/src/mbgl/style/style_layer.cpp @@ -14,7 +14,7 @@ bool StyleLayer::isBackground() const { return type == StyleLayerType::Background; } -void StyleLayer::setClasses(const std::vector<std::string> &class_names, const timestamp now, +void StyleLayer::setClasses(const std::vector<std::string> &class_names, const std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition) { // Stores all keys that we have already added transitions for. std::set<PropertyKey> already_applied; @@ -45,8 +45,8 @@ void StyleLayer::setClasses(const std::vector<std::string> &class_names, const t if (appliedProperties.mostRecent() != ClassID::Fallback) { // This property key hasn't been set by a previous class, so we need to add a transition // to the fallback value for that key. - const timestamp begin = now + defaultTransition.delay * 1_millisecond; - const timestamp end = begin + defaultTransition.duration * 1_millisecond; + const std::chrono::steady_clock::time_point begin = now + defaultTransition.delay; + const std::chrono::steady_clock::time_point end = begin + defaultTransition.duration; const PropertyValue &value = PropertyFallbackValue::Get(key); appliedProperties.add(ClassID::Fallback, begin, end, value); } @@ -55,7 +55,7 @@ void StyleLayer::setClasses(const std::vector<std::string> &class_names, const t // Helper function for applying all properties of a a single class that haven't been applied yet. void StyleLayer::applyClassProperties(const ClassID class_id, - std::set<PropertyKey> &already_applied, timestamp now, + std::set<PropertyKey> &already_applied, std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition) { auto style_it = styles.find(class_id); if (style_it == styles.end()) { @@ -83,8 +83,8 @@ void StyleLayer::applyClassProperties(const ClassID class_id, if (appliedProperties.mostRecent() != class_id) { const PropertyTransition &transition = class_properties.getTransition(key, defaultTransition); - const timestamp begin = now + transition.delay * 1_millisecond; - const timestamp end = begin + transition.duration * 1_millisecond; + const std::chrono::steady_clock::time_point begin = now + transition.delay; + const std::chrono::steady_clock::time_point end = begin + transition.duration; const PropertyValue &value = property_pair.second; appliedProperties.add(class_id, begin, end, value); } @@ -120,7 +120,7 @@ private: }; template <typename T> -void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const timestamp now, const ZoomHistory &zoomHistory) { +void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) { auto it = appliedStyle.find(key); if (it != appliedStyle.end()) { AppliedClassProperties &applied = it->second; @@ -138,7 +138,7 @@ void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, c } template <typename T> -void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const timestamp now, const ZoomHistory &zoomHistory) { +void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) { auto it = appliedStyle.find(key); if (it != appliedStyle.end()) { AppliedClassProperties &applied = it->second; @@ -150,7 +150,7 @@ void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, cons target = mapbox::util::apply_visitor(evaluator, property.value); } else if (now >= property.begin) { // We overwrite the current property partially with the new value. - float progress = float(now - property.begin) / float(property.end - property.begin); + float progress = std::chrono::duration<float>(now - property.begin) / (property.end - property.begin); target = util::interpolate(target, mapbox::util::apply_visitor(evaluator, property.value), progress); } else { // Do not apply this property because its transition hasn't begun yet. @@ -160,7 +160,7 @@ void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, cons } template <> -void StyleLayer::applyStyleProperties<FillProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) { +void StyleLayer::applyStyleProperties<FillProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) { properties.set<FillProperties>(); FillProperties &fill = properties.get<FillProperties>(); applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, now, zoomHistory); @@ -174,7 +174,7 @@ void StyleLayer::applyStyleProperties<FillProperties>(const float z, const times } template <> -void StyleLayer::applyStyleProperties<LineProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) { +void StyleLayer::applyStyleProperties<LineProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) { properties.set<LineProperties>(); LineProperties &line = properties.get<LineProperties>(); applyTransitionedStyleProperty(PropertyKey::LineOpacity, line.opacity, z, now, zoomHistory); @@ -189,11 +189,11 @@ void StyleLayer::applyStyleProperties<LineProperties>(const float z, const times applyStyleProperty(PropertyKey::LineImage, line.image, z, now, zoomHistory); // for scaling dasharrays - applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), now + 10000, zoomHistory); + applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), std::chrono::steady_clock::time_point::max(), zoomHistory); } template <> -void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) { +void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) { properties.set<SymbolProperties>(); SymbolProperties &symbol = properties.get<SymbolProperties>(); applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now, zoomHistory); @@ -219,7 +219,7 @@ void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const tim } template <> -void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) { +void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) { properties.set<RasterProperties>(); RasterProperties &raster = properties.get<RasterProperties>(); applyTransitionedStyleProperty(PropertyKey::RasterOpacity, raster.opacity, z, now, zoomHistory); @@ -232,7 +232,7 @@ void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const tim } template <> -void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const timestamp now, const ZoomHistory &zoomHistory) { +void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) { properties.set<BackgroundProperties>(); BackgroundProperties &background = properties.get<BackgroundProperties>(); applyTransitionedStyleProperty(PropertyKey::BackgroundOpacity, background.opacity, z, now, zoomHistory); @@ -240,7 +240,7 @@ void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const applyStyleProperty(PropertyKey::BackgroundImage, background.image, z, now, zoomHistory); } -void StyleLayer::updateProperties(float z, const timestamp now, ZoomHistory &zoomHistory) { +void StyleLayer::updateProperties(float z, const std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory) { cleanupAppliedStyleProperties(now); switch (type) { @@ -263,7 +263,7 @@ bool StyleLayer::hasTransitions() const { } -void StyleLayer::cleanupAppliedStyleProperties(timestamp now) { +void StyleLayer::cleanupAppliedStyleProperties(std::chrono::steady_clock::time_point now) { auto it = appliedStyle.begin(); const auto end = appliedStyle.end(); while (it != end) { diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp index 8531ad3a05..69af1dc230 100644 --- a/src/mbgl/style/style_layer.hpp +++ b/src/mbgl/style/style_layer.hpp @@ -13,6 +13,7 @@ #include <string> #include <map> #include <set> +#include <chrono> namespace mbgl { @@ -36,10 +37,10 @@ public: // Updates the StyleProperties information in this layer by evaluating all // pending transitions and applied classes in order. - void updateProperties(float z, timestamp now, ZoomHistory &zoomHistory); + void updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory); // Sets the list of classes and creates transitions to the currently applied values. - void setClasses(const std::vector<std::string> &class_names, timestamp now, + void setClasses(const std::vector<std::string> &class_names, std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition); bool hasTransitions() const; @@ -47,16 +48,16 @@ public: private: // Applies all properties from a class, if they haven't been applied already. void applyClassProperties(ClassID class_id, std::set<PropertyKey> &already_applied, - timestamp now, const PropertyTransition &defaultTransition); + std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition); // Sets the properties of this object by evaluating all pending transitions and // aplied classes in order. - template <typename T> void applyStyleProperties(float z, timestamp now, const ZoomHistory &zoomHistory); - template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, timestamp now, const ZoomHistory &zoomHistory); - template <typename T> void applyTransitionedStyleProperty(PropertyKey key, T &, float z, timestamp now, const ZoomHistory &zoomHistory); + template <typename T> void applyStyleProperties(float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory); + template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory); + template <typename T> void applyTransitionedStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory); // Removes all expired style transitions. - void cleanupAppliedStyleProperties(timestamp now); + void cleanupAppliedStyleProperties(std::chrono::steady_clock::time_point now); public: // The name of this layer. diff --git a/src/mbgl/style/style_layer_group.cpp b/src/mbgl/style/style_layer_group.cpp index 5ca020ee74..f57ec5cc7b 100644 --- a/src/mbgl/style/style_layer_group.cpp +++ b/src/mbgl/style/style_layer_group.cpp @@ -2,7 +2,7 @@ namespace mbgl { -void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, timestamp now, +void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition) { for (const util::ptr<StyleLayer> &layer : layers) { if (layer) { @@ -11,10 +11,10 @@ void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, ti } } -void StyleLayerGroup::updateProperties(float z, timestamp t, ZoomHistory &zoomHistory) { +void StyleLayerGroup::updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory) { for (const util::ptr<StyleLayer> &layer: layers) { if (layer) { - layer->updateProperties(z, t, zoomHistory); + layer->updateProperties(z, now, zoomHistory); } } } diff --git a/src/mbgl/style/style_layer_group.hpp b/src/mbgl/style/style_layer_group.hpp index 85cfcc6dd6..79b75a55e9 100644 --- a/src/mbgl/style/style_layer_group.hpp +++ b/src/mbgl/style/style_layer_group.hpp @@ -4,14 +4,15 @@ #include <mbgl/style/style_layer.hpp> #include <vector> +#include <chrono> namespace mbgl { class StyleLayerGroup { public: - void setClasses(const std::vector<std::string> &class_names, timestamp now, + void setClasses(const std::vector<std::string> &class_names, std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition); - void updateProperties(float z, timestamp t, ZoomHistory &zoomHistory); + void updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory); bool hasTransitions() const; public: diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 454e786573..1fc57e2f7e 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -341,7 +341,7 @@ std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value) { return std::tuple<bool, Function<T>> { true, StopsFunction<T>(std::get<1>(stops), base) }; } -template <typename T> inline timestamp defaultDurationValue() { return 300_milliseconds; } +template <typename T> inline std::chrono::duration<float> defaultDurationValue() { return std::chrono::duration<float>(300.0f); } template <typename T> std::tuple<bool, FadedStopsFunction<T>> StyleParser::parseFadedStopsFunction(JSVal value) { @@ -350,13 +350,12 @@ std::tuple<bool, FadedStopsFunction<T>> StyleParser::parseFadedStopsFunction(JSV return std::tuple<bool, FadedStopsFunction<T>> { false, {} }; } - timestamp duration = defaultDurationValue<T>(); + std::chrono::duration<float> duration = defaultDurationValue<T>(); if (value.HasMember("duration")) { JSVal value_duration = value["duration"]; if (value_duration.IsNumber()) { - float duration_ = value_duration.GetDouble(); - duration = 1_millisecond * duration_; + duration = static_cast<std::chrono::duration<float>>(value_duration.GetDouble()); } else { Log::Warning(Event::ParseStyle, "duration must be numeric"); } @@ -381,6 +380,18 @@ bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKe } template<typename T> +void StyleParser::parseVisibility(StyleBucket &bucket, JSVal value) { + if (!value.HasMember("visibility")) { + return; + } else if (!value["visibility"].IsString()) { + Log::Warning(Event::ParseStyle, "value of 'visibility' must be a string"); + bucket.visibility = VisibilityType::Visible; + return; + } + bucket.visibility = VisibilityTypeClass({ value["visibility"].GetString(), value["visibility"].GetStringLength() }); +} + +template<typename T> bool StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) { if (!value.HasMember(property_name)) { return false; @@ -398,6 +409,15 @@ template<> std::tuple<bool, std::string> StyleParser::parseProperty(JSVal value, return std::tuple<bool, std::string> { true, { value.GetString(), value.GetStringLength() } }; } +template<> std::tuple<bool, bool> StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsBool()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a boolean", property_name); + return std::tuple<bool, bool> { false, true }; + } + + return std::tuple<bool, bool> { true, value.GetBool() }; +} + template<> std::tuple<bool, TranslateAnchorType> StyleParser::parseProperty(JSVal value, const char *property_name) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); @@ -416,18 +436,81 @@ template<> std::tuple<bool, RotateAnchorType> StyleParser::parseProperty<RotateA return std::tuple<bool, RotateAnchorType> { true, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; } +template<> std::tuple<bool, CapType> StyleParser::parseProperty<CapType>(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple<bool, CapType> { false, CapType::Butt }; + } + + return std::tuple<bool, CapType> { true, CapTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple<bool, JoinType> StyleParser::parseProperty<JoinType>(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple<bool, JoinType> { false, JoinType::Miter }; + } + + return std::tuple<bool, JoinType> { true, JoinTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple<bool, PlacementType> StyleParser::parseProperty<PlacementType>(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple<bool, PlacementType> { false, PlacementType::Point }; + } + + return std::tuple<bool, PlacementType> { true, PlacementTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple<bool, TextAnchorType> StyleParser::parseProperty<TextAnchorType>(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple<bool, TextAnchorType> { false, TextAnchorType::Center }; + } + + return std::tuple<bool, TextAnchorType> { true, TextAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple<bool, TextJustifyType> StyleParser::parseProperty<TextJustifyType>(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple<bool, TextJustifyType> { false, TextJustifyType::Center }; + } + + return std::tuple<bool, TextJustifyType> { true, TextJustifyTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple<bool, TextTransformType> StyleParser::parseProperty<TextTransformType>(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple<bool, TextTransformType> { false, TextTransformType::None }; + } + + return std::tuple<bool, TextTransformType> { true, TextTransformTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple<bool, RotationAlignmentType> StyleParser::parseProperty<RotationAlignmentType>(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple<bool, RotationAlignmentType> { false, RotationAlignmentType::Map }; + } + + return std::tuple<bool, RotationAlignmentType> { true, RotationAlignmentTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + template<> std::tuple<bool, PropertyTransition> StyleParser::parseProperty(JSVal value, const char */*property_name*/) { PropertyTransition transition; if (value.IsObject()) { if (value.HasMember("duration") && value["duration"].IsNumber()) { - transition.duration = value["duration"].GetUint(); + transition.duration = std::chrono::milliseconds(value["duration"].GetUint()); } if (value.HasMember("delay") && value["delay"].IsNumber()) { - transition.delay = value["delay"].GetUint(); + transition.delay = std::chrono::milliseconds(value["delay"].GetUint()); } } - if (transition.duration == 0 && transition.delay == 0) { + if (transition.duration == std::chrono::steady_clock::duration::zero() && transition.delay == std::chrono::steady_clock::duration::zero()) { return std::tuple<bool, PropertyTransition> { false, std::move(transition) }; } @@ -710,6 +793,50 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) { parseOptionalProperty<FadedStopsFunction<Faded<std::string>>>("background-image", Key::BackgroundImage, klass, value); } +void StyleParser::parseLayout(JSVal value, util::ptr<StyleBucket> &bucket) { + using Key = PropertyKey; + + parseVisibility<VisibilityType>(*bucket, value); + + parseOptionalProperty<CapType>("line-cap", Key::LineCap, bucket->layout, value); + parseOptionalProperty<JoinType>("line-join", Key::LineJoin, bucket->layout, value); + parseOptionalProperty<Function<float>>("line-miter-limit", Key::LineMiterLimit, bucket->layout, value); + parseOptionalProperty<Function<float>>("line-round-limit", Key::LineRoundLimit, bucket->layout, value); + + parseOptionalProperty<PlacementType>("symbol-placement", Key::SymbolPlacement, bucket->layout, value); + parseOptionalProperty<Function<float>>("symbol-min-distance", Key::SymbolMinDistance, bucket->layout, value); + parseOptionalProperty<bool>("symbol-avoid-edges", Key::SymbolAvoidEdges, bucket->layout, value); + parseOptionalProperty<bool>("icon-allow-overlap", Key::IconAllowOverlap, bucket->layout, value); + parseOptionalProperty<bool>("icon-ignore-placement", Key::IconIgnorePlacement, bucket->layout, value); + parseOptionalProperty<bool>("icon-optional", Key::IconOptional, bucket->layout, value); + parseOptionalProperty<RotationAlignmentType>("icon-rotation-alignment", Key::IconRotationAlignment, bucket->layout, value); + parseOptionalProperty<Function<float>>("icon-max-size", Key::IconMaxSize, bucket->layout, value); + parseOptionalProperty<std::string>("icon-image", Key::IconImage, bucket->layout, value); + parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, bucket->layout, value); + parseOptionalProperty<Function<float>>("icon-padding", Key::IconPadding, bucket->layout, value); + parseOptionalProperty<bool>("icon-keep-upright", Key::IconKeepUpright, bucket->layout, value); + parseOptionalProperty<Function<float>>("icon-offset", { Key::IconOffsetX, Key::IconOffsetY }, bucket->layout, value); + parseOptionalProperty<RotationAlignmentType>("text-rotation-alignment", Key::TextRotationAlignment, bucket->layout, value); + parseOptionalProperty<std::string>("text-field", Key::TextField, bucket->layout, value); + parseOptionalProperty<std::string>("text-font", Key::TextFont, bucket->layout, value); + parseOptionalProperty<Function<float>>("text-max-size", Key::TextMaxSize, bucket->layout, value); + parseOptionalProperty<Function<float>>("text-max-width", Key::TextMaxWidth, bucket->layout, value); + parseOptionalProperty<Function<float>>("text-line-height", Key::TextLineHeight, bucket->layout, value); + parseOptionalProperty<Function<float>>("text-letter-spacing", Key::TextLetterSpacing, bucket->layout, value); + parseOptionalProperty<TextJustifyType>("text-justify", Key::TextJustify, bucket->layout, value); + parseOptionalProperty<TextAnchorType>("text-anchor", Key::TextAnchor, bucket->layout, value); + parseOptionalProperty<Function<float>>("text-max-angle", Key::TextMaxAngle, bucket->layout, value); + parseOptionalProperty<Function<float>>("text-rotate", Key::TextRotate, bucket->layout, value); + parseOptionalProperty<Function<float>>("text-padding", Key::TextPadding, bucket->layout, value); + parseOptionalProperty<bool>("text-keep-upright", Key::TextKeepUpright, bucket->layout, value); + parseOptionalProperty<TextTransformType>("text-transform", Key::TextTransform, bucket->layout, value); + parseOptionalProperty<Function<float>>("text-offset", { Key::TextOffsetX, Key::TextOffsetY }, bucket->layout, value); + parseOptionalProperty<bool>("text-allow-overlap", Key::TextAllowOverlap, bucket->layout, value); + parseOptionalProperty<bool>("text-ignore-placement", Key::TextIgnorePlacement, bucket->layout, value); + parseOptionalProperty<bool>("text-optional", Key::TextOptional, bucket->layout, value); + +} + void StyleParser::parseReference(JSVal value, util::ptr<StyleLayer> &layer) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "layer ref of '%s' must be a string", layer->id.c_str()); @@ -772,7 +899,7 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) { if (value.HasMember("layout")) { JSVal value_render = replaceConstant(value["layout"]); - parseLayout(value_render, layer); + parseLayout(value_render, layer->bucket); } if (value.HasMember("minzoom")) { @@ -794,89 +921,6 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) { } } -void StyleParser::parseLayout(JSVal value, util::ptr<StyleLayer> &layer) { - if (!value.IsObject()) { - Log::Warning(Event::ParseStyle, "layout property of layer '%s' must be an object", layer->id.c_str()); - return; - } - - StyleBucket &bucket = *layer->bucket; - parseRenderProperty<VisibilityTypeClass>(value, bucket.visibility, "visibility"); - - switch (layer->type) { - case StyleLayerType::Fill: { - StyleBucketFill &render = bucket.render.get<StyleBucketFill>(); - - parseRenderProperty<WindingTypeClass>(value, render.winding, "fill-winding"); - } break; - - case StyleLayerType::Line: { - StyleBucketLine &render = bucket.render.get<StyleBucketLine>(); - - parseRenderProperty<CapTypeClass>(value, render.cap, "line-cap"); - parseRenderProperty<JoinTypeClass>(value, render.join, "line-join"); - parseRenderProperty(value, render.miter_limit, "line-miter-limit"); - parseRenderProperty(value, render.round_limit, "line-round-limit"); - } break; - - case StyleLayerType::Symbol: { - StyleBucketSymbol &render = bucket.render.get<StyleBucketSymbol>(); - - parseRenderProperty<PlacementTypeClass>(value, render.placement, "symbol-placement"); - if (render.placement == PlacementType::Line) { - // Change the default value in case of line placement. - render.text.rotation_alignment = RotationAlignmentType::Map; - render.icon.rotation_alignment = RotationAlignmentType::Map; - } - - parseRenderProperty(value, render.min_distance, "symbol-min-distance"); - parseRenderProperty(value, render.avoid_edges, "symbol-avoid-edges"); - - parseRenderProperty(value, render.icon.allow_overlap, "icon-allow-overlap"); - parseRenderProperty(value, render.icon.ignore_placement, "icon-ignore-placement"); - parseRenderProperty(value, render.icon.optional, "icon-optional"); - parseRenderProperty<RotationAlignmentTypeClass>(value, render.icon.rotation_alignment, "icon-rotation-alignment"); - parseRenderProperty(value, render.icon.max_size, "icon-max-size"); - parseRenderProperty(value, render.icon.image, "icon-image"); - parseRenderProperty(value, render.icon.rotate, "icon-rotate"); - parseRenderProperty(value, render.icon.padding, "icon-padding"); - parseRenderProperty(value, render.icon.keep_upright, "icon-keep-upright"); - parseRenderProperty(value, render.icon.offset, "icon-offset"); - - - parseRenderProperty<RotationAlignmentTypeClass>(value, render.text.rotation_alignment, "text-rotation-alignment"); - parseRenderProperty(value, render.text.field, "text-field"); - parseRenderProperty(value, render.text.font, "text-font"); - parseRenderProperty(value, render.text.max_size, "text-max-size"); - if (parseRenderProperty(value, render.text.max_width, "text-max-width")) { - render.text.max_width *= 24; // em - } - if (parseRenderProperty(value, render.text.line_height, "text-line-height")) { - render.text.line_height *= 24; // em - } - if (parseRenderProperty(value, render.text.letter_spacing, "text-letter-spacing")) { - render.text.letter_spacing *= 24; // em - } - parseRenderProperty<TextJustifyTypeClass>(value, render.text.justify, "text-justify"); - parseRenderProperty<TextAnchorTypeClass>(value, render.text.anchor, "text-anchor"); - parseRenderProperty(value, render.text.max_angle, "text-max-angle"); - parseRenderProperty(value, render.text.rotate, "text-rotate"); - parseRenderProperty(value, render.text.slant, "text-slant"); - parseRenderProperty(value, render.text.padding, "text-padding"); - parseRenderProperty(value, render.text.keep_upright, "text-keep-upright"); - parseRenderProperty<TextTransformTypeClass>(value, render.text.transform, "text-transform"); - parseRenderProperty(value, render.text.offset, "text-offset"); - parseRenderProperty(value, render.text.allow_overlap, "text-allow-overlap"); - parseRenderProperty(value, render.text.ignore_placement, "text-ignore-placement"); - parseRenderProperty(value, render.text.optional, "text-optional"); - } break; - - default: - // There are no render properties for these layer types. - break; - } -} - void StyleParser::parseSprite(JSVal value) { if (value.IsString()) { sprite = { value.GetString(), value.GetStringLength() }; diff --git a/src/mbgl/style/style_parser.hpp b/src/mbgl/style/style_parser.hpp index 482fa0a00b..5b55d6116e 100644 --- a/src/mbgl/style/style_parser.hpp +++ b/src/mbgl/style/style_parser.hpp @@ -53,7 +53,7 @@ private: void parsePaint(JSVal, ClassProperties &properties); void parseReference(JSVal value, util::ptr<StyleLayer> &layer); void parseBucket(JSVal value, util::ptr<StyleLayer> &layer); - void parseLayout(JSVal value, util::ptr<StyleLayer> &layer); + void parseLayout(JSVal value, util::ptr<StyleBucket> &bucket); void parseSprite(JSVal value); void parseGlyphURL(JSVal value); @@ -65,6 +65,8 @@ private: // Parses optional properties into style class properties. template <typename T> + void parseVisibility(StyleBucket &bucket, JSVal value); + template <typename T> bool parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value); template <typename T> bool parseOptionalProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value); diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp index cd9c29d30a..b7d675857a 100644 --- a/src/mbgl/style/types.hpp +++ b/src/mbgl/style/types.hpp @@ -72,18 +72,6 @@ MBGL_DEFINE_ENUM_CLASS(VisibilityTypeClass, VisibilityType, { // ------------------------------------------------------------------------------------------------- -enum class WindingType : bool { - EvenOdd, - NonZero, -}; - -MBGL_DEFINE_ENUM_CLASS(WindingTypeClass, WindingType, { - { WindingType::EvenOdd, "even-odd" }, - { WindingType::NonZero, "non-zero" }, -}); - -// ------------------------------------------------------------------------------------------------- - enum class CapType : uint8_t { Round, Butt, diff --git a/src/mbgl/style/zoom_history.hpp b/src/mbgl/style/zoom_history.hpp index 7031068624..eee0d9b443 100644 --- a/src/mbgl/style/zoom_history.hpp +++ b/src/mbgl/style/zoom_history.hpp @@ -1,8 +1,7 @@ #ifndef MBGL_STYLE_ZOOM_HISTORY #define MBGL_STYLE_ZOOM_HISTORY -#include <mbgl/util/time.hpp> - +#include <chrono> #include <cmath> namespace mbgl { @@ -10,15 +9,15 @@ namespace mbgl { struct ZoomHistory { float lastZoom; float lastIntegerZoom; - timestamp lastIntegerZoomTime; + std::chrono::steady_clock::time_point lastIntegerZoomTime; bool first = true; - void update(float z, timestamp now) { + void update(float z, std::chrono::steady_clock::time_point now) { if (first) { first = false; lastIntegerZoom = std::floor(z); - lastIntegerZoomTime = 0; + lastIntegerZoomTime = std::chrono::steady_clock::time_point::min(); lastZoom = z; } diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp index 2f5db180fd..0d9e70d556 100644 --- a/src/mbgl/text/glyph_store.cpp +++ b/src/mbgl/text/glyph_store.cpp @@ -148,28 +148,23 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl }); // The prepare call jumps back to the main thread. - fileSource.prepare([&, url] { - auto request = fileSource.request(ResourceType::Glyphs, url); - request->onload([&, url](const Response &res) { - if (res.code != 200) { - // Something went wrong with loading the glyph pbf. Pass on the error to the future listeners. - const std::string msg = std::string { "[ERROR] failed to load glyphs (" } + util::toString(res.code) + "): " + res.message; - promise.set_exception(std::make_exception_ptr(std::runtime_error(msg))); - } else { - // Transfer the data to the GlyphSet and signal its availability. - // Once it is available, the caller will need to call parse() to actually - // parse the data we received. We are not doing this here since this callback is being - // called from another (unknown) thread. - data = res.data; - promise.set_value(*this); - } - }); - request->oncancel([&]() { - promise.set_exception(std::make_exception_ptr(std::runtime_error("Loading glyphs was canceled"))); - }); + fileSource.request({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) { + if (res.status != Response::Successful) { + // Something went wrong with loading the glyph pbf. Pass on the error to the future listeners. + const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + res.message; + promise.set_exception(std::make_exception_ptr(std::runtime_error(msg))); + } else { + // Transfer the data to the GlyphSet and signal its availability. + // Once it is available, the caller will need to call parse() to actually + // parse the data we received. We are not doing this here since this callback is being + // called from another (unknown) thread. + data = res.data; + promise.set_value(*this); + } }); } + std::shared_future<GlyphPBF &> GlyphPBF::getFuture() { return future; } diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index bc0f642c48..27e2b7010f 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -131,8 +131,8 @@ GlyphBox getMergedBoxes(const GlyphBoxes &glyphs, const Anchor &anchor) { Placement Placement::getIcon(Anchor &anchor, const Rect<uint16_t> &image, float boxScale, const std::vector<Coordinate> &line, const StyleBucketSymbol &props) { - const float dx = props.icon.offset.x; - const float dy = props.icon.offset.y; + const float dx = props.icon.offset[0]; + const float dy = props.icon.offset[1]; float x1 = dx - image.originalW / 2.0f; float x2 = x1 + image.w; float y1 = dy - image.originalH / 2.0f; diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp deleted file mode 100644 index d6d6370546..0000000000 --- a/src/mbgl/util/compression.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include <mbgl/util/compression.hpp> - -#include <zlib.h> - -#include <cstring> -#include <stdexcept> - -namespace mbgl { -namespace util { - -std::string compress(const std::string &raw) { - z_stream deflate_stream; - memset(&deflate_stream, 0, sizeof(deflate_stream)); - - // TODO: reuse z_streams - if (deflateInit(&deflate_stream, Z_DEFAULT_COMPRESSION) != Z_OK) { - throw std::runtime_error("failed to initialize deflate"); - } - - deflate_stream.next_in = (Bytef *)raw.data(); - deflate_stream.avail_in = uInt(raw.size()); - - std::string result; - char out[16384]; - - int code; - do { - deflate_stream.next_out = reinterpret_cast<Bytef *>(out); - deflate_stream.avail_out = sizeof(out); - code = deflate(&deflate_stream, Z_FINISH); - if (result.size() < deflate_stream.total_out) { - // append the block to the output string - result.append(out, deflate_stream.total_out - result.size()); - } - } while (code == Z_OK); - - deflateEnd(&deflate_stream); - - if (code != Z_STREAM_END) { - throw std::runtime_error(deflate_stream.msg); - } - - return result; -} - -std::string decompress(const std::string &raw) { - z_stream inflate_stream; - memset(&inflate_stream, 0, sizeof(inflate_stream)); - - // TODO: reuse z_streams - if (inflateInit(&inflate_stream) != Z_OK) { - throw std::runtime_error("failed to initialize inflate"); - } - - inflate_stream.next_in = (Bytef *)raw.data(); - inflate_stream.avail_in = uInt(raw.size()); - - std::string result; - char out[15384]; - - int code; - do { - inflate_stream.next_out = reinterpret_cast<Bytef *>(out); - inflate_stream.avail_out = sizeof(out); - code = inflate(&inflate_stream, 0); - // result.append(out, sizeof(out) - inflate_stream.avail_out); - if (result.size() < inflate_stream.total_out) { - result.append(out, inflate_stream.total_out - result.size()); - } - } while (code == Z_OK); - - inflateEnd(&inflate_stream); - - if (code != Z_STREAM_END) { - throw std::runtime_error(inflate_stream.msg); - } - - return result; -} -} -} diff --git a/src/mbgl/util/compression.hpp b/src/mbgl/util/compression.hpp deleted file mode 100644 index a33b2476a7..0000000000 --- a/src/mbgl/util/compression.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef MBGL_UTIL_COMPRESSION -#define MBGL_UTIL_COMPRESSION - -#include <string> - -namespace mbgl { -namespace util { - -std::string compress(const std::string &raw); -std::string decompress(const std::string &raw); - -} -} - -#endif diff --git a/src/mbgl/util/merge_lines.cpp b/src/mbgl/util/merge_lines.cpp new file mode 100644 index 0000000000..7eb8306707 --- /dev/null +++ b/src/mbgl/util/merge_lines.cpp @@ -0,0 +1,99 @@ +#include "merge_lines.hpp" +#include <sstream> + +namespace mbgl { +namespace util { + +unsigned int mergeFromRight(std::vector<SymbolFeature> &features, + std::map<std::string, unsigned int> &rightIndex, + std::map<std::string, unsigned int>::iterator left, + std::string &rightKey, + std::vector<std::vector<Coordinate>> &geom) { + + unsigned int index = left->second; + rightIndex.erase(left); + rightIndex[rightKey] = index; + features[index].geometry[0].pop_back(); + features[index].geometry[0].insert( + features[index].geometry[0].end(), geom[0].begin(), geom[0].end()); + geom[0].clear(); + return index; +} + +unsigned int mergeFromLeft(std::vector<SymbolFeature> &features, + std::map<std::string, unsigned int> &leftIndex, + std::string &leftKey, + std::map<std::string, unsigned int>::iterator right, + std::vector<std::vector<Coordinate>> &geom) { + + unsigned int index = right->second; + leftIndex.erase(right); + leftIndex[leftKey] = index; + geom[0].pop_back(); + geom[0].insert( + geom[0].end(), features[index].geometry[0].begin(), features[index].geometry[0].end()); + features[index].geometry[0].clear(); + std::swap(features[index].geometry[0], geom[0]); + return index; +} + +std::string +getKey(const std::u32string &text, const std::vector<std::vector<Coordinate>> &geom, bool onRight) { + const Coordinate &coord = onRight ? geom[0].back() : geom[0].front(); + std::ostringstream key; + for (const char32_t &c : text) { + key << (char)c; + } + key << ":" << coord.x << ":" << coord.y; + return key.str(); +} + +void mergeLines(std::vector<SymbolFeature> &features) { + + std::map<std::string, unsigned int> leftIndex; + std::map<std::string, unsigned int> rightIndex; + + for (unsigned int k = 0; k < features.size(); k++) { + SymbolFeature &feature = features[k]; + std::vector<std::vector<Coordinate>> &geometry = feature.geometry; + + if (!feature.label.length()) { + continue; + } + + std::string leftKey = getKey(feature.label, geometry, false); + std::string rightKey = getKey(feature.label, geometry, true); + + auto left = rightIndex.find(leftKey); + auto right = leftIndex.find(rightKey); + + if ((left != rightIndex.end()) && (right != leftIndex.end()) && + (left->second != right->second)) { + // found lines with the same text adjacent to both ends of the current line, merge all + // three + unsigned int j = mergeFromLeft(features, leftIndex, leftKey, right, geometry); + unsigned int i = + mergeFromRight(features, rightIndex, left, rightKey, features[j].geometry); + + leftIndex.erase(leftKey); + rightIndex.erase(rightKey); + rightIndex[getKey(feature.label, features[i].geometry, true)] = i; + + } else if (left != rightIndex.end()) { + // found mergeable line adjacent to the start of the current line, merge + mergeFromRight(features, rightIndex, left, rightKey, geometry); + + } else if (right != leftIndex.end()) { + // found mergeable line adjacent to the end of the current line, merge + mergeFromLeft(features, leftIndex, leftKey, right, geometry); + + } else { + // no adjacent lines, add as a new item + leftIndex[leftKey] = k; + rightIndex[rightKey] = k; + } + } +} + +} // end namespace util +} // end namespace mbgl diff --git a/src/mbgl/util/merge_lines.hpp b/src/mbgl/util/merge_lines.hpp index e0dd4c6415..4460a0bcea 100644 --- a/src/mbgl/util/merge_lines.hpp +++ b/src/mbgl/util/merge_lines.hpp @@ -4,102 +4,24 @@ #include <map> #include <string> #include <vector> -#include <sstream> #include <mbgl/renderer/symbol_bucket.hpp> - namespace mbgl { namespace util { -unsigned int mergeFromRight( - std::vector<SymbolFeature> &features, - std::map<std::string,unsigned int> &rightIndex, - std::map<std::string,unsigned int>::iterator left, - std::string &rightKey, - std::vector<std::vector<Coordinate>> &geom) { - - unsigned int index = left->second; - rightIndex.erase(left); - rightIndex[rightKey] = index; - features[index].geometry[0].pop_back(); - features[index].geometry[0].insert(features[index].geometry[0].end(), geom[0].begin(), geom[0].end()); - geom[0].clear(); - return index; -} - -unsigned int mergeFromLeft( - std::vector<SymbolFeature> &features, - std::map<std::string,unsigned int> &leftIndex, - std::string &leftKey, - std::map<std::string,unsigned int>::iterator right, - std::vector<std::vector<Coordinate>> &geom) { - - unsigned int index = right->second; - leftIndex.erase(right); - leftIndex[leftKey] = index; - geom[0].pop_back(); - geom[0].insert(geom[0].end(), features[index].geometry[0].begin(), features[index].geometry[0].end()); - features[index].geometry[0].clear(); - std::swap(features[index].geometry[0], geom[0]); - return index; -} - -std::string getKey(const std::u32string &text, const std::vector<std::vector<Coordinate>> &geom, bool onRight) { - const Coordinate &coord = onRight ? geom[0].back() : geom[0].front(); - std::ostringstream key; - for (const char32_t &c : text) { - key << (char)c; - } - key << ":" << coord.x << ":" << coord.y; - return key.str(); -} - - -void mergeLines(std::vector<SymbolFeature> &features) { - - std::map<std::string,unsigned int> leftIndex; - std::map<std::string,unsigned int> rightIndex; - - for (unsigned int k = 0; k < features.size(); k++) { - SymbolFeature &feature = features[k]; - std::vector<std::vector<Coordinate>> &geometry = feature.geometry; - - if (!feature.label.length()) { - continue; - } - - std::string leftKey = getKey(feature.label, geometry, false); - std::string rightKey = getKey(feature.label, geometry, true); - - auto left = rightIndex.find(leftKey); - auto right = leftIndex.find(rightKey); - - if ((left != rightIndex.end()) && (right != leftIndex.end()) && (left->second != right->second)) { - // found lines with the same text adjacent to both ends of the current line, merge all three - unsigned int j = mergeFromLeft(features, leftIndex, leftKey, right, geometry); - unsigned int i = mergeFromRight(features, rightIndex, left, rightKey, features[j].geometry); - - leftIndex.erase(leftKey); - rightIndex.erase(rightKey); - rightIndex[getKey(feature.label, features[i].geometry, true)] = i; - - } else if (left != rightIndex.end()) { - // found mergeable line adjacent to the start of the current line, merge - mergeFromRight(features, rightIndex, left, rightKey, geometry); - - } else if (right != leftIndex.end()) { - // found mergeable line adjacent to the end of the current line, merge - mergeFromLeft(features, leftIndex, leftKey, right, geometry); - - } else { - // no adjacent lines, add as a new item - leftIndex[leftKey] = k; - rightIndex[rightKey] = k; - } +unsigned int mergeFromRight(std::vector<SymbolFeature> &features, + std::map<std::string, unsigned int> &rightIndex, + std::map<std::string, unsigned int>::iterator left, + std::string &rightKey, + std::vector<std::vector<Coordinate>> &geom); - } +unsigned int mergeFromLeft(std::vector<SymbolFeature> &features, + std::map<std::string, unsigned int> &leftIndex, + std::string &leftKey, + std::map<std::string, unsigned int>::iterator right, + std::vector<std::vector<Coordinate>> &geom); -} +void mergeLines(std::vector<SymbolFeature> &features); } // end namespace util } // end namespace mbgl diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp index 3049d92289..724936972c 100644 --- a/src/mbgl/util/raster.cpp +++ b/src/mbgl/util/raster.cpp @@ -2,7 +2,6 @@ #include <mbgl/platform/gl.hpp> #include <mbgl/util/raster.hpp> -#include <mbgl/util/time.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/util/std.hpp> @@ -91,15 +90,15 @@ void Raster::bind(const GLuint custom_texture) { } void Raster::beginFadeInTransition() { - timestamp start = util::now(); - fade_transition = std::make_shared<util::ease_transition<double>>(opacity, 1.0, opacity, start, 250_milliseconds); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + fade_transition = std::make_shared<util::ease_transition<double>>(opacity, 1.0, opacity, start, std::chrono::milliseconds(250)); } bool Raster::needsTransition() const { return fade_transition != nullptr; } -void Raster::updateTransitions(timestamp now) { +void Raster::updateTransitions(std::chrono::steady_clock::time_point now) { if (fade_transition->update(now) == util::transition::complete) { fade_transition = nullptr; } diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp index bbdfc5321d..f9e275461c 100644 --- a/src/mbgl/util/raster.hpp +++ b/src/mbgl/util/raster.hpp @@ -8,6 +8,7 @@ #include <string> #include <mutex> +#include <chrono> typedef struct uv_loop_s uv_loop_t; @@ -34,7 +35,7 @@ public: // transitions void beginFadeInTransition(); bool needsTransition() const; - void updateTransitions(timestamp now); + void updateTransitions(std::chrono::steady_clock::time_point now); public: // loaded image dimensions diff --git a/src/mbgl/util/sqlite3.cpp b/src/mbgl/util/sqlite3.cpp deleted file mode 100644 index 787db83992..0000000000 --- a/src/mbgl/util/sqlite3.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include <mbgl/util/sqlite3.hpp> -#include <sqlite3.h> - -#include <cassert> - -namespace mapbox { -namespace sqlite { - -Database::Database(const std::string &filename, int flags) { - const int err = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr); - if (err != SQLITE_OK) { - Exception ex { err, sqlite3_errmsg(db) }; - db = nullptr; - throw ex; - } -} - -Database::Database(Database &&other) - : db(std::move(other.db)) {} - -Database &Database::operator=(Database &&other) { - std::swap(db, other.db); - return *this; -} - -Database::~Database() { - if (db) { - const int err = sqlite3_close(db); - if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(db) }; - } - } -} - -Database::operator bool() const { - return db != nullptr; -} - -void Database::exec(const std::string &sql) { - assert(db); - char *msg = nullptr; - const int err = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &msg); - if (msg) { - Exception ex { err, msg }; - sqlite3_free(msg); - throw ex; - } else if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(db) }; - } -} - -Statement Database::prepare(const char *query) { - assert(db); - return std::move(Statement(db, query)); -} - -Statement::Statement(sqlite3 *db, const char *sql) { - const int err = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); - if (err != SQLITE_OK) { - stmt = nullptr; - throw Exception { err, sqlite3_errmsg(db) }; - } -} - -#define CHECK_SQLITE_OK(err) \ - if (err != SQLITE_OK) { \ - throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; \ - } - -Statement::Statement(Statement &&other) { - *this = std::move(other); -} - -Statement &Statement::operator=(Statement &&other) { - std::swap(stmt, other.stmt); - return *this; -} - -Statement::~Statement() { - if (stmt) { - const int err = sqlite3_finalize(stmt); - CHECK_SQLITE_OK(err) - } -} - -Statement::operator bool() const { - return stmt != nullptr; -} - -#define BIND_3(type, value) \ - assert(stmt); \ - const int err = sqlite3_bind_##type(stmt, offset, value); \ - CHECK_SQLITE_OK(err) - -#define BIND_5(type, value, length, param) \ - assert(stmt); \ - const int err = sqlite3_bind_##type(stmt, offset, value, length, param); \ - CHECK_SQLITE_OK(err) - -template <> void Statement::bind(int offset, int value) { - BIND_3(int, value) -} - -template <> void Statement::bind(int offset, int64_t value) { - BIND_3(int64, value) -} - -template <> void Statement::bind(int offset, double value) { - BIND_3(double, value) -} - -template <> void Statement::bind(int offset, bool value) { - BIND_3(int, value) -} - -template <> void Statement::bind(int offset, const char *value) { - BIND_5(text, value, -1, nullptr) -} - -void Statement::bind(int offset, const std::string &value, bool retain) { - BIND_5(blob, value.data(), int(value.size()), retain ? SQLITE_TRANSIENT : SQLITE_STATIC) -} - -bool Statement::run() { - assert(stmt); - const int err = sqlite3_step(stmt); - if (err == SQLITE_DONE) { - return false; - } else if (err == SQLITE_ROW) { - return true; - } else { - throw std::runtime_error("failed to run statement"); - } -} - -template <> int Statement::get(int offset) { - assert(stmt); - return sqlite3_column_int(stmt, offset); -} - -template <> int64_t Statement::get(int offset) { - assert(stmt); - return sqlite3_column_int64(stmt, offset); -} - -template <> double Statement::get(int offset) { - assert(stmt); - return sqlite3_column_double(stmt, offset); -} - -template <> std::string Statement::get(int offset) { - assert(stmt); - return { - reinterpret_cast<const char *>(sqlite3_column_blob(stmt, offset)), - size_t(sqlite3_column_bytes(stmt, offset)) - }; -} - -void Statement::reset() { - assert(stmt); - sqlite3_reset(stmt); -} - -} -} diff --git a/src/mbgl/util/sqlite3.hpp b/src/mbgl/util/sqlite3.hpp deleted file mode 100644 index 3e324f7ce1..0000000000 --- a/src/mbgl/util/sqlite3.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include <string> -#include <stdexcept> - -typedef struct sqlite3 sqlite3; -typedef struct sqlite3_stmt sqlite3_stmt; - -namespace mapbox { -namespace sqlite { - -enum OpenFlag : int { - ReadOnly = 0x00000001, - ReadWrite = 0x00000002, - Create = 0x00000004, - NoMutex = 0x00008000, - FullMutex = 0x00010000, - SharedCache = 0x00020000, - PrivateCache = 0x00040000, -}; - -struct Exception : std::runtime_error { - inline Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {} - const int code = 0; -}; - -class Statement; - -class Database { -private: - Database(const Database &) = delete; - Database &operator=(const Database &) = delete; - -public: - Database(const std::string &filename, int flags = 0); - Database(Database &&); - ~Database(); - Database &operator=(Database &&); - - operator bool() const; - - void exec(const std::string &sql); - Statement prepare(const char *query); - -private: - sqlite3 *db = nullptr; -}; - -class Statement { -private: - Statement(const Statement &) = delete; - Statement &operator=(const Statement &) = delete; - -public: - Statement(sqlite3 *db, const char *sql); - Statement(Statement &&); - ~Statement(); - Statement &operator=(Statement &&); - - operator bool() const; - - template <typename T> void bind(int offset, T value); - void bind(int offset, const std::string &value, bool retain = true); - template <typename T> T get(int offset); - - bool run(); - void reset(); - -private: - sqlite3_stmt *stmt = nullptr; -}; - -} -} diff --git a/src/mbgl/util/stopwatch.cpp b/src/mbgl/util/stopwatch.cpp index 14b4f4018b..310bdb8ff2 100644 --- a/src/mbgl/util/stopwatch.cpp +++ b/src/mbgl/util/stopwatch.cpp @@ -1,6 +1,5 @@ #ifndef DISABLE_STOPWATCH #include <mbgl/util/stopwatch.hpp> -#include <mbgl/util/time.hpp> #include <mbgl/util/string.hpp> #include <mbgl/platform/log.hpp> @@ -10,21 +9,21 @@ using namespace mbgl::util; stopwatch::stopwatch(Event event_) - : event(event_), start(now()) {} + : event(event_), start(std::chrono::steady_clock::now()) {} stopwatch::stopwatch(EventSeverity severity_, Event event_) - : severity(severity_), event(event_), start(now()) {} + : severity(severity_), event(event_), start(std::chrono::steady_clock::now()) {} stopwatch::stopwatch(const std::string &name_, Event event_) - : name(name_), event(event_), start(now()) {} + : name(name_), event(event_), start(std::chrono::steady_clock::now()) {} stopwatch::stopwatch(const std::string &name_, EventSeverity severity_, Event event_) - : name(name_), severity(severity_), event(event_), start(now()) {} + : name(name_), severity(severity_), event(event_), start(std::chrono::steady_clock::now()) {} void stopwatch::report(const std::string &name_) { - timestamp duration = now() - start; + std::chrono::steady_clock::duration duration = std::chrono::steady_clock::now() - start; - Log::Record(severity, event, name_ + ": " + util::toString(double(duration) / 1_millisecond) + "ms"); + Log::Record(severity, event, name_ + ": " + util::toString(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()) + "ms"); start += duration; } diff --git a/src/mbgl/util/stopwatch.hpp b/src/mbgl/util/stopwatch.hpp index 663bbb6fc7..bfa384c07e 100644 --- a/src/mbgl/util/stopwatch.hpp +++ b/src/mbgl/util/stopwatch.hpp @@ -4,6 +4,7 @@ #include <mbgl/platform/event.hpp> #include <string> +#include <chrono> namespace mbgl { namespace util { @@ -22,7 +23,7 @@ private: const std::string name; EventSeverity severity = EventSeverity::Debug; Event event = Event::General; - uint64_t start; + std::chrono::steady_clock::time_point start; }; #else class stopwatch { diff --git a/src/mbgl/util/time.cpp b/src/mbgl/util/time.cpp index 1953975b18..94936acff0 100644 --- a/src/mbgl/util/time.cpp +++ b/src/mbgl/util/time.cpp @@ -4,10 +4,6 @@ namespace mbgl { namespace util { -timestamp now() { - return uv_hrtime(); -} - static const char *week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; diff --git a/src/mbgl/util/transition.cpp b/src/mbgl/util/transition.cpp index e63a5c311f..c47776c9b5 100644 --- a/src/mbgl/util/transition.cpp +++ b/src/mbgl/util/transition.cpp @@ -10,7 +10,7 @@ UnitBezier ease(0, 0, 0.25, 1); transition::~transition() {} template <typename T> -transition::state ease_transition<T>::update(timestamp now) const { +transition::state ease_transition<T>::update(std::chrono::steady_clock::time_point now) const { float t = progress(now); if (t >= 1) { value = to; diff --git a/src/mbgl/util/transition.hpp b/src/mbgl/util/transition.hpp index d6bbc9eba0..fc1ecd23c3 100644 --- a/src/mbgl/util/transition.hpp +++ b/src/mbgl/util/transition.hpp @@ -2,7 +2,7 @@ #define MBGL_UTIL_TRANSITION #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/time.hpp> +#include <chrono> namespace mbgl { namespace util { @@ -14,34 +14,35 @@ public: complete }; - inline transition(timestamp start_, timestamp duration_) + inline transition(std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_) : start(start_), duration(duration_) {} - inline float progress(timestamp now) const { - if (duration == 0) return 1; + inline float progress(std::chrono::steady_clock::time_point now) const { + if (duration == std::chrono::steady_clock::duration::zero()) return 1; if (start > now) return 0; - return (float)(now - start) / duration; + return std::chrono::duration<float>(now - start) / duration; } - virtual state update(timestamp now) const = 0; + virtual state update(std::chrono::steady_clock::time_point now) const = 0; virtual ~transition(); protected: - const timestamp start, duration; + const std::chrono::steady_clock::time_point start; + const std::chrono::steady_clock::duration duration; }; template <typename T> class ease_transition : public transition { public: - ease_transition(T from_, T to_, T& value_, timestamp start_, timestamp duration_) + ease_transition(T from_, T to_, T& value_, std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_) : transition(start_, duration_), from(from_), to(to_), value(value_) {} - state update(timestamp now) const; + state update(std::chrono::steady_clock::time_point now) const; private: const T from, to; @@ -52,12 +53,12 @@ private: template <typename T> class timeout : public transition { public: - timeout(T final_value_, T& value_, timestamp start_, timestamp duration_) + timeout(T final_value_, T& value_, std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_) : transition(start_, duration_), final_value(final_value_), value(value_) {} - state update(timestamp now) const { + state update(std::chrono::steady_clock::time_point now) const { if (progress(now) >= 1) { value = final_value; return complete; diff --git a/src/mbgl/util/uv-worker.c b/src/mbgl/util/uv-worker.c index d2aa908019..ec8eae461c 100644 --- a/src/mbgl/util/uv-worker.c +++ b/src/mbgl/util/uv-worker.c @@ -25,7 +25,7 @@ void uv__worker_free_messenger(uv_messenger_t *msgr) { void uv__worker_thread_finished(uv__worker_thread_t *worker_thread) { uv_worker_t *worker = worker_thread->worker; -#ifndef NDEBUG +#ifdef DEBUG assert(uv_thread_self() == worker->thread_id); #endif @@ -101,7 +101,7 @@ void uv__worker_thread_loop(void *ptr) { } int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name) { -#ifndef NDEBUG +#ifdef DEBUG worker->thread_id = uv_thread_self(); #endif worker->loop = loop; @@ -134,7 +134,7 @@ int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char * void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb, uv_worker_after_cb after_work_cb) { -#ifndef NDEBUG +#ifdef DEBUG assert(uv_thread_self() == worker->thread_id); #endif @@ -155,7 +155,7 @@ void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb, } void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb) { -#ifndef NDEBUG +#ifdef DEBUG assert(uv_thread_self() == worker->thread_id); #endif diff --git a/src/mbgl/util/uv-worker.h b/src/mbgl/util/uv-worker.h index cb2075d1c3..5640bfd14d 100644 --- a/src/mbgl/util/uv-worker.h +++ b/src/mbgl/util/uv-worker.h @@ -17,7 +17,7 @@ typedef void (*uv_worker_after_cb)(void *data); typedef void (*uv_worker_close_cb)(uv_worker_t *worker); struct uv_worker_s { -#ifndef NDEBUG +#ifdef DEBUG unsigned long thread_id; #endif uv_loop_t *loop; diff --git a/src/mbgl/util/uv.cpp b/src/mbgl/util/uv.cpp index 7aa5bad0cf..a993e6b962 100644 --- a/src/mbgl/util/uv.cpp +++ b/src/mbgl/util/uv.cpp @@ -22,4 +22,12 @@ std::string cwd() { #endif } +const char *getFileRequestError(uv_fs_t *req) { +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + return uv_strerror(uv_last_error(req->loop)); +#else + return uv_strerror(int(req->result)); +#endif +} + } |