diff --git a/include/llmr/map/map.hpp b/include/llmr/map/map.hpp index f54773a52..19a57ab52 100644 --- a/include/llmr/map/map.hpp +++ b/include/llmr/map/map.hpp @@ -65,8 +65,8 @@ private: bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list& retain); bool findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list& retain); bool updateTiles(); - std::shared_ptr addTile(const Tile::ID& id); - std::shared_ptr hasTile(const Tile::ID& id); + const Tile *addTile(const Tile::ID& id); + const Tile *hasTile(const Tile::ID& id); void update(); @@ -82,8 +82,8 @@ private: float pixel_ratio; - std::forward_list> tiles; - std::forward_list> historic_tiles; + std::forward_list tiles; + std::forward_list> tile_data; }; } diff --git a/include/llmr/map/tile.hpp b/include/llmr/map/tile.hpp index bd0505f5c..922940d74 100644 --- a/include/llmr/map/tile.hpp +++ b/include/llmr/map/tile.hpp @@ -2,19 +2,34 @@ #define LLMR_MAP_TILE #include +#include +#include #include #include namespace llmr { -class Tile { +class TileData; + +class Tile : private util::noncopyable { public: typedef vec3 ID; public: + Tile(const ID& id); + static ID parent(const ID& id, int32_t z); + static ID normalize(const ID& id); static std::forward_list children(const ID& id, int32_t z); + + +public: + const Tile::ID id; + uint8_t clip_id; + mat4 matrix; + std::shared_ptr data; + }; } diff --git a/include/llmr/map/tile_data.hpp b/include/llmr/map/tile_data.hpp index 05dc59d42..67aa3e530 100644 --- a/include/llmr/map/tile_data.hpp +++ b/include/llmr/map/tile_data.hpp @@ -71,7 +71,6 @@ public: public: const Tile::ID id; - uint8_t clip_id; std::atomic state; // Holds the actual geometries in this tile. @@ -89,8 +88,6 @@ public: // Holds the buckets of this tile. // They contain the location offsets in the buffers stored above std::map> buckets; - - mat4 matrix; private: // Source data std::string data; diff --git a/include/llmr/renderer/painter.hpp b/include/llmr/renderer/painter.hpp index 76a8d788b..6556cae87 100644 --- a/include/llmr/renderer/painter.hpp +++ b/include/llmr/renderer/painter.hpp @@ -33,7 +33,7 @@ public: void setup(); void clear(); void changeMatrix(); - void render(const std::shared_ptr& tile); + void render(const Tile& tile); void renderMatte(); void renderBackground(); void renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id); diff --git a/src/map/coverage.cpp b/src/map/coverage.cpp index e65e3469d..d1ead268e 100644 --- a/src/map/coverage.cpp +++ b/src/map/coverage.cpp @@ -69,11 +69,10 @@ std::forward_list> llmr::covering_tiles(int32_t zoom, const std::forward_list> tiles; auto scanLine = [&tiles, zoom](int32_t x0, int32_t x1, int32_t y, int32_t ymax) { - int32_t x, wx; + int32_t x; if (y >= 0 && y <= ymax) { for (x = x0; x < x1; x++) { - wx = (x + ymax) % ymax; - tiles.emplace_front(wx, y, zoom); + tiles.emplace_front(x, y, zoom); } } }; diff --git a/src/map/map.cpp b/src/map/map.cpp index 41d574527..8ab9f7f64 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -238,29 +238,42 @@ void Map::update() { } -TileData::Ptr Map::hasTile(const Tile::ID& id) { - for (TileData::Ptr& tile : tiles) { - if (tile->id == id) { - return tile; +const Tile *Map::hasTile(const Tile::ID& id) { + for (const Tile& tile : tiles) { + if (tile.id == id) { + return &tile; } } - return TileData::Ptr(); + return nullptr; } -TileData::Ptr Map::addTile(const Tile::ID& id) { - TileData::Ptr tile = hasTile(id); +const Tile *Map::addTile(const Tile::ID& id) { + const Tile *tile = hasTile(id); - if (!tile.get()) { - // We couldn't find the tile in the list. Create a new one. - tile = std::make_shared(id, style); - assert(tile); - // std::cerr << "init " << id.z << "/" << id.x << "/" << id.y << std::endl; - // std::cerr << "add " << tile->toString() << std::endl; - tiles.push_front(tile); + if (tile) { + return tile; } - return tile; + tiles.emplace_front(id); + Tile& new_tile = tiles.front(); + + // We couldn't find the tile in the list. Create a new one. + // Try to find the associated TileData object. + const Tile::ID normalized_id = Tile::normalize(id); + + auto it = std::find_if(tile_data.begin(), tile_data.end(), [&normalized_id](const std::weak_ptr& tile_data) { + return !tile_data.expired() && tile_data.lock()->id == normalized_id; + }); + if (it != tile_data.end() && !it->expired()) { + // There is already a tile data object and it's not yet expired, so we're + // going to reuse it. + new_tile.data = it->lock(); + } else { + new_tile.data = std::make_shared(normalized_id, style); + tile_data.push_front(new_tile.data); + } + return &new_tile; } /** @@ -278,9 +291,8 @@ bool Map::findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::f auto ids = Tile::children(id, z + 1); for (const Tile::ID& child_id : ids) { - const TileData::Ptr& tile = hasTile(child_id); - if (tile && tile->state == TileData::parsed) { - assert(tile); + const Tile *tile = hasTile(child_id); + if (tile && tile->data && tile->data->state == TileData::parsed) { retain.emplace_front(tile->id); } else { complete = false; @@ -305,9 +317,8 @@ bool Map::findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::f bool Map::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list& retain) { for (int32_t z = id.z - 1; z >= minCoveringZoom; --z) { const Tile::ID parent_id = Tile::parent(id, z); - const TileData::Ptr tile = hasTile(parent_id); - if (tile && tile->state == TileData::parsed) { - assert(tile); + const Tile *tile = hasTile(parent_id); + if (tile && tile->data && tile->data->state == TileData::parsed) { retain.emplace_front(tile->id); return true; } @@ -344,10 +355,10 @@ bool Map::updateTiles() { // Add existing child/parent tiles if the actual tile is not yet loaded for (const Tile::ID& id : required) { - TileData::Ptr tile = addTile(id); + const Tile *tile = addTile(id); assert(tile); - if (tile->state != TileData::parsed) { + if (tile && tile->data && tile->data->state != TileData::parsed) { // The tile we require is not yet loaded. Try to find a parent or // child tile that we already have. @@ -362,20 +373,18 @@ bool Map::updateTiles() { } } - if (tile->state == TileData::initial) { + if (tile && tile->data && tile->data->state == TileData::initial) { // If the tile is new, we have to make sure to load it. - tile->request(); + tile->data->request(); changed = true; } } // Remove tiles that we definitely don't need, i.e. tiles that are not on // the required list. - tiles.remove_if([&retain, &changed](const TileData::Ptr& tile) { - assert(tile); - bool obsolete = std::find(retain.begin(), retain.end(), tile->id) == retain.end(); + tiles.remove_if([&retain, &changed](const Tile & tile) { + bool obsolete = std::find(retain.begin(), retain.end(), tile.id) == retain.end(); if (obsolete) { - tile->cancel(); changed = true; } return obsolete; @@ -384,8 +393,13 @@ bool Map::updateTiles() { // Sort tiles by zoom level, front to back. // We're painting front-to-back, so we want to draw more detailed tiles first // before filling in other parts with lower zoom levels. - tiles.sort([](const TileData::Ptr& a, const TileData::Ptr& b) { - return a->id.z > b->id.z; + tiles.sort([](const Tile & a, const Tile & b) { + return a.id.z > b.id.z; + }); + + // Remove all the expired pointers from the list. + tile_data.remove_if([](const std::weak_ptr& tile_data) { + return tile_data.expired(); }); return changed; @@ -406,20 +420,19 @@ bool Map::render() { // the stencil buffer. uint8_t i = 1; painter.prepareClippingMask(); - for (TileData::Ptr& tile : tiles) { - assert(tile); - if (tile->state == TileData::parsed) { + for (Tile& tile : tiles) { + if (tile.data && tile.data->state == TileData::parsed) { // The position matrix. - transform.matrixFor(tile->matrix, tile->id); - matrix::multiply(tile->matrix, painter.projMatrix, tile->matrix); - tile->clip_id = i++; - painter.drawClippingMask(tile->matrix, tile->clip_id); + transform.matrixFor(tile.matrix, tile.id); + matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix); + tile.clip_id = i++; + painter.drawClippingMask(tile.matrix, tile.clip_id); } } painter.finishClippingMask(); - for (TileData::Ptr& tile : tiles) { - if (tile->state == TileData::parsed) { + for (const Tile& tile : tiles) { + if (tile.data && tile.data->state == TileData::parsed) { painter.render(tile); } } diff --git a/src/map/tile.cpp b/src/map/tile.cpp index 9dd2ad582..3447fb384 100644 --- a/src/map/tile.cpp +++ b/src/map/tile.cpp @@ -4,6 +4,10 @@ using namespace llmr; +Tile::Tile(const ID& id) + : id(id) { +} + Tile::ID Tile::parent(const ID& id, int32_t z) { assert(z < id.z); ID pos(id); @@ -29,3 +33,11 @@ std::forward_list Tile::children(const ID& id, int32_t z) { return children; } + +Tile::ID Tile::normalize(const ID& id) { + ID pos(id); + int32_t dim = pow(2, id.z); + while (pos.x < 0) pos.x += dim; + while (pos.x >= dim) pos.x -= dim; + return pos; +} \ No newline at end of file diff --git a/src/map/tile_data.cpp b/src/map/tile_data.cpp index 5b69b1d8a..7df8183ba 100644 --- a/src/map/tile_data.cpp +++ b/src/map/tile_data.cpp @@ -83,9 +83,9 @@ bool TileData::parse() { parseStyleLayers(tile, style.layers); double parse_time = (platform::time() - parse_time_start) * 1000.0; if (state == obsolete) { - fprintf(stderr, "[%p] parsing [%d/%d/%d] cancelled after %8.3fms\n", this, id.z, id.x, id.y, parse_time); + // fprintf(stderr, "[%p] parsing [%d/%d/%d] cancelled after %8.3fms\n", this, id.z, id.x, id.y, parse_time); } else { - fprintf(stderr, "[%p] parsing [%d/%d/%d] took %8.3fms\n", this, id.z, id.x, id.y, parse_time); + // fprintf(stderr, "[%p] parsing [%d/%d/%d] took %8.3fms\n", this, id.z, id.x, id.y, parse_time); } } catch (const std::exception& ex) { fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what()); diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp index d6f02cc34..9a4ffe4a2 100644 --- a/src/renderer/painter.cpp +++ b/src/renderer/painter.cpp @@ -130,24 +130,25 @@ void Painter::clear() { glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } -void Painter::render(const TileData::Ptr& tile) { - if (tile->state != TileData::parsed) { +void Painter::render(const Tile& tile) { + assert(tile.data); + if (tile.data->state != TileData::parsed) { return; } - matrix = tile->matrix; - glStencilFunc(GL_EQUAL, tile->clip_id, 0xFF); + matrix = tile.matrix; + glStencilFunc(GL_EQUAL, tile.clip_id, 0xFF); - renderLayers(tile, style.layers); + renderLayers(tile.data, style.layers); if (settings.debug) { - renderDebug(tile); + renderDebug(tile.data); } renderBackground(); } -void Painter::renderLayers(const std::shared_ptr& tile, const std::vector& layers) { +void Painter::renderLayers(const std::shared_ptr& tile_data, const std::vector& layers) { // Render everything top-to-bottom by using reverse iterators typedef std::vector::const_reverse_iterator iterator; for (iterator it = layers.rbegin(), end = layers.rend(); it != end; ++it) { @@ -156,15 +157,15 @@ void Painter::renderLayers(const std::shared_ptr& tile, const std::vec if (layer_desc.child_layer.size()) { // This is a layer group. // TODO: create framebuffer - renderLayers(tile, layer_desc.child_layer); + renderLayers(tile_data, layer_desc.child_layer); // TODO: render framebuffer on previous framebuffer } else { // This is a singular layer. Try to find the bucket associated with // this layer and render it. - auto bucket_it = tile->buckets.find(layer_desc.bucket_name); - if (bucket_it != tile->buckets.end()) { + auto bucket_it = tile_data->buckets.find(layer_desc.bucket_name); + if (bucket_it != tile_data->buckets.end()) { assert(bucket_it->second); - bucket_it->second->render(*this, layer_desc.name, tile->id); + bucket_it->second->render(*this, layer_desc.name, tile_data->id); } } } @@ -423,7 +424,7 @@ void Painter::renderPoint(PointBucket& bucket, const std::string& layer_name, co bucket.drawPoints(*pointShader); } -void Painter::renderDebug(const TileData::Ptr& tile) { +void Painter::renderDebug(const TileData::Ptr& tile_data) { // Disable depth test and don't count this towards the depth buffer, // but *don't* disable stencil test, as we want to clip the red tile border // to the tile viewport. @@ -442,13 +443,13 @@ void Painter::renderDebug(const TileData::Ptr& tile) { glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index()); // draw debug info - tile->debugFontArray.bind(*plainShader, tile->debugFontBuffer, BUFFER_OFFSET(0)); + tile_data->debugFontArray.bind(*plainShader, tile_data->debugFontBuffer, BUFFER_OFFSET(0)); plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f); lineWidth(4.0f * transform.pixelRatio); - glDrawArrays(GL_LINES, 0, (GLsizei)tile->debugFontBuffer.index()); + glDrawArrays(GL_LINES, 0, (GLsizei)tile_data->debugFontBuffer.index()); plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f); lineWidth(2.0f * transform.pixelRatio); - glDrawArrays(GL_LINES, 0, (GLsizei)tile->debugFontBuffer.index()); + glDrawArrays(GL_LINES, 0, (GLsizei)tile_data->debugFontBuffer.index()); // Revert blending mode to blend to the back. glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);