Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] Symbol cross-fading.
Browse files Browse the repository at this point in the history
 Hold onto tiles after they've been removed from the render tree long enough to run a fade animation on their symbols.
  • Loading branch information
ChrisLoer committed Nov 17, 2017
1 parent d69c1c4 commit 25cfb58
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 4 deletions.
23 changes: 23 additions & 0 deletions src/mbgl/renderer/renderer_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
} else {
std::sort(sortedTiles.begin(), sortedTiles.end(),
[](const auto& a, const auto& b) { return a.get().id < b.get().id; });
// Don't render non-symbol layers for tiles that we're only holding on to for symbol fading
sortedTiles.erase(std::remove_if(sortedTiles.begin(), sortedTiles.end(),
[](const auto& tile) { return tile.get().tile.holdForFade(); }),
sortedTiles.end());
}

std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
Expand Down Expand Up @@ -389,6 +393,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
}

placement->setRecent(parameters.timePoint);

updateFadingTiles();
} else {
placement->setStale();
}
Expand Down Expand Up @@ -754,10 +760,27 @@ bool Renderer::Impl::hasTransitions(TimePoint timePoint) const {
if (placement->hasTransitions(timePoint)) {
return true;
}

if (fadingTiles) {
return true;
}

return false;
}

void Renderer::Impl::updateFadingTiles() {
fadingTiles = false;
for (auto& source : renderSources) {
for (auto& renderTile : source.second->getRenderTiles()) {
Tile& tile = renderTile.get().tile;
if (tile.holdForFade()) {
fadingTiles = true;
tile.performedFadePlacement();
}
}
}
}

bool Renderer::Impl::isLoaded() const {
for (const auto& entry: renderSources) {
if (!entry.second->isLoaded()) {
Expand Down
3 changes: 3 additions & 0 deletions src/mbgl/renderer/renderer_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class Renderer::Impl : public GlyphManagerObserver,
void onTileChanged(RenderSource&, const OverscaledTileID&) override;
void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;

void updateFadingTiles();

friend class Renderer;

RendererBackend& backend;
Expand Down Expand Up @@ -113,6 +115,7 @@ class Renderer::Impl : public GlyphManagerObserver,
std::unique_ptr<Placement> placement;

bool contextLost = false;
bool fadingTiles = false;
};

} // namespace mbgl
20 changes: 20 additions & 0 deletions src/mbgl/renderer/tile_pyramid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,17 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
}
return tiles.emplace(tileID, std::move(tile)).first->second.get();
};

std::map<UnwrappedTileID, Tile*> previouslyRenderedTiles;
for (auto& renderTile : renderTiles) {
previouslyRenderedTiles[renderTile.id] = &renderTile.tile;
}

auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) {
renderTiles.emplace_back(tileID, tile);
rendered.emplace(tileID);
previouslyRenderedTiles.erase(tileID); // Still rendering this tile, no need for special fading logic.
tile.markRenderedIdeal();
};

renderTiles.clear();
Expand All @@ -162,6 +170,18 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer

algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn,
idealTiles, zoomRange, tileZoom);

for (auto previouslyRenderedTile : previouslyRenderedTiles) {
Tile& tile = *previouslyRenderedTile.second;
tile.markRenderedPreviously();
if (tile.holdForFade()) {
// Since it was rendered in the last frame, we know we have it
// Don't mark the tile "Required" to avoid triggering a new network request
retainTileFn(tile, TileNecessity::Optional);
renderTiles.emplace_back(previouslyRenderedTile.first, tile);
rendered.emplace(previouslyRenderedTile.first);
}
}

if (type != SourceType::Annotations) {
size_t conservativeCacheSize =
Expand Down
19 changes: 16 additions & 3 deletions src/mbgl/text/placement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri
std::unordered_set<uint32_t> seenCrossTileIDs;

for (RenderTile& renderTile : symbolLayer.renderTiles) {

if (!renderTile.tile.isRenderable()) {
continue;
}
Expand Down Expand Up @@ -78,7 +77,7 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri
state,
pixelsToTileUnits);

placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs);
placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs, renderTile.tile.holdForFade());
}
}

Expand All @@ -90,7 +89,8 @@ void Placement::placeLayerBucket(
const float scale,
const float textPixelRatio,
const bool showCollisionBoxes,
std::unordered_set<uint32_t>& seenCrossTileIDs) {
std::unordered_set<uint32_t>& seenCrossTileIDs,
const bool holdingForFade) {

auto partiallyEvaluatedTextSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom());
auto partiallyEvaluatedIconSize = bucket.iconSizeBinder->evaluateForZoom(state.getZoom());
Expand All @@ -101,6 +101,13 @@ void Placement::placeLayerBucket(
for (auto& symbolInstance : bucket.symbolInstances) {

if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) {
if (holdingForFade) {
// Mark all symbols from this tile as "not placed", but don't add to seenCrossTileIDs, because we don't
// know yet if we have a duplicate in a parent tile that _should_ be placed.
placements.emplace(symbolInstance.crossTileID, JointPlacement(false, false, false));
continue;
}

bool placeText = false;
bool placeIcon = false;
bool offscreen = true;
Expand Down Expand Up @@ -152,6 +159,12 @@ void Placement::placeLayerBucket(

assert(symbolInstance.crossTileID != 0);

if (placements.find(symbolInstance.crossTileID) != placements.end()) {
// If there's a previous placement with this ID, it comes from a tile that's fading out
// Erase it so that the placement result from the non-fading tile supersedes it
placements.erase(symbolInstance.crossTileID);
}

placements.emplace(symbolInstance.crossTileID, JointPlacement(placeText, placeIcon, offscreen));
seenCrossTileIDs.insert(symbolInstance.crossTileID);
}
Expand Down
3 changes: 2 additions & 1 deletion src/mbgl/text/placement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class Placement {
const float scale,
const float pixelRatio,
const bool showCollisionBoxes,
std::unordered_set<uint32_t>& seenCrossTileIDs);
std::unordered_set<uint32_t>& seenCrossTileIDs,
const bool holdingForFade);

void updateBucketOpacities(SymbolBucket&, std::set<uint32_t>&);

Expand Down
21 changes: 21 additions & 0 deletions src/mbgl/tile/geometry_tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,25 @@ void GeometryTile::resetCrossTileIDs() {
}
}

bool GeometryTile::holdForFade() const {
return mode == MapMode::Continuous &&
(fadeState == FadeState::NeedsFirstPlacement || fadeState == FadeState::NeedsSecondPlacement);
}

void GeometryTile::markRenderedIdeal() {
fadeState = FadeState::Loaded;
}
void GeometryTile::markRenderedPreviously() {
if (fadeState == FadeState::Loaded) {
fadeState = FadeState::NeedsFirstPlacement;
}
}
void GeometryTile::performedFadePlacement() {
if (fadeState == FadeState::NeedsFirstPlacement) {
fadeState = FadeState::NeedsSecondPlacement;
} else if (fadeState == FadeState::NeedsSecondPlacement) {
fadeState = FadeState::CanRemove;
}
}

} // namespace mbgl
13 changes: 13 additions & 0 deletions src/mbgl/tile/geometry_tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ class GeometryTile : public Tile, public GlyphRequestor, ImageRequestor {

void resetCrossTileIDs() override;

bool holdForFade() const override;
void markRenderedIdeal() override;
void markRenderedPreviously() override;
void performedFadePlacement() override;

protected:
const GeometryTileData* getData() {
return data.get();
Expand Down Expand Up @@ -130,7 +135,15 @@ class GeometryTile : public Tile, public GlyphRequestor, ImageRequestor {
const MapMode mode;

bool showCollisionBoxes;

enum class FadeState {
Loaded,
NeedsFirstPlacement,
NeedsSecondPlacement,
CanRemove
};

FadeState fadeState = FadeState::Loaded;
public:
optional<gl::Texture> glyphAtlasTexture;
optional<gl::Texture> iconAtlasTexture;
Expand Down
14 changes: 14 additions & 0 deletions src/mbgl/tile/tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ class Tile : private util::noncopyable {
return loaded && !pending;
}

// "holdForFade" is used to keep tiles in the render tree after they're no longer
// ideal tiles in order to allow symbols to fade out
virtual bool holdForFade() const {
return false;
}
// Set whenever this tile is used as an ideal tile
virtual void markRenderedIdeal() {}
// Set when the tile is removed from the ideal render set but may still be held for fading
virtual void markRenderedPreviously() {}
// Placement operation performed while this tile is fading
// We hold onto a tile for two placements: fading starts with the first placement
// and will have time to finish by the second placement.
virtual void performedFadePlacement() {}

virtual void resetCrossTileIDs() {};

void dumpDebugLogs() const;
Expand Down

0 comments on commit 25cfb58

Please sign in to comment.