diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp index d9d4fdf54f5..7bf137d5add 100644 --- a/src/mbgl/text/glyph_atlas.cpp +++ b/src/mbgl/text/glyph_atlas.cpp @@ -52,37 +52,34 @@ bool GlyphAtlas::hasGlyphRange(const FontStack& fontStack, const GlyphRange& ran } void GlyphAtlas::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies) { - // Figure out which glyph ranges need to be fetched - GlyphRangeDependencies missing; - for (const auto& fontStackToGlyphIDs : glyphDependencies) { - auto fontStackEntry = entries.find(fontStackToGlyphIDs.first); - for (auto glyphID : fontStackToGlyphIDs.second) { + auto dependencies = std::make_shared(std::move(glyphDependencies)); + + // Figure out which glyph ranges need to be fetched. For each range that does need to + // be fetched, record an entry mapping the requestor to a shared pointer containing the + // dependencies. When the shared pointer becomes unique, we know that all the dependencies + // for that requestor have been fetched, and can notify it of completion. + for (const auto& dependency : *dependencies) { + const FontStack& fontStack = dependency.first; + const GlyphIDs& glyphIDs = dependency.second; + + Entry& entry = entries[fontStack]; + + for (const auto& glyphID : glyphIDs) { GlyphRange range = getGlyphRange(glyphID); - if (fontStackEntry == entries.end() || - !rangeIsParsed(fontStackEntry->second.ranges, range)) { - missing[fontStackToGlyphIDs.first].insert(range); + if (!rangeIsParsed(entry.ranges, range)) { + entry.ranges.emplace(std::piecewise_construct, + std::forward_as_tuple(range), + std::forward_as_tuple(this, fontStack, range, this, fileSource)); + + entry.ranges.find(range)->second.requestors[&requestor] = dependencies; } } } - - if (missing.empty()) { - // Send "glyphs available" message immediately to requestor - addGlyphs(requestor, glyphDependencies); - requestor.onGlyphsAvailable(getGlyphPositions(glyphDependencies)); - } else { - // Trigger network requests for glyphs, send "glyphs available" message when all requests have finished - tileDependencies.emplace(std::piecewise_construct, - std::forward_as_tuple(&requestor), - std::forward_as_tuple(missing, std::move(glyphDependencies))); - for (auto fontStackToGlyphIDs : missing) { - for (auto& range : fontStackToGlyphIDs.second) { - entries[fontStackToGlyphIDs.first].ranges.emplace(std::piecewise_construct, - std::forward_as_tuple(range), - std::forward_as_tuple(this, fontStackToGlyphIDs.first, range, this, fileSource)); - - entries[fontStackToGlyphIDs.first].ranges.find(range)->second.addRequestor(requestor); - } - } + + // If the shared dependencies pointer is already unique, then all dependent glyph ranges + // have already been loaded. Send a notification immediately. + if (dependencies.unique()) { + addGlyphs(requestor, *dependencies); } } @@ -97,67 +94,57 @@ void GlyphAtlas::onGlyphsError(const FontStack& fontStack, const GlyphRange& ran } void GlyphAtlas::onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& range) { - auto glyphPBF = entries[fontStack].ranges.find(range); - if (glyphPBF != entries[fontStack].ranges.end()) { - auto glyphRequestors = glyphPBF->second.processRequestors(); - for (auto requestor : glyphRequestors) { - auto tileDependency = tileDependencies.find(requestor); - if (tileDependency != tileDependencies.end()) { - auto fontRanges = tileDependency->second.pendingRanges.find(fontStack); - if (fontRanges != tileDependency->second.pendingRanges.end()) { - fontRanges->second.erase(range); - if (fontRanges->second.empty()) { - tileDependency->second.pendingRanges.erase(fontRanges); - } - } - if (tileDependency->second.pendingRanges.empty()) { - addGlyphs(*requestor, tileDependency->second.glyphDependencies); - tileDependency->first->onGlyphsAvailable(getGlyphPositions(tileDependency->second.glyphDependencies)); - tileDependencies.erase(requestor); - } + Entry& entry = entries[fontStack]; + + auto it = entry.ranges.find(range); + if (it != entry.ranges.end()) { + for (auto& pair : it->second.requestors) { + GlyphRequestor& requestor = *pair.first; + const std::shared_ptr& dependencies = pair.second; + if (dependencies.unique()) { + addGlyphs(requestor, *dependencies); } } + it->second.requestors.clear(); } + if (observer) { observer->onGlyphsLoaded(fontStack, range); } } - -// Builds up the set of glyph positions needed for a given tile; result is handed to worker threads -GlyphPositionMap GlyphAtlas::getGlyphPositions(const GlyphDependencies& glyphDependencies) const { +void GlyphAtlas::addGlyphs(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies) { GlyphPositionMap glyphPositions; - for (const auto& fontStackToGlyphIDs : glyphDependencies) { - auto entry = entries.find(fontStackToGlyphIDs.first); - if (entry != entries.end()) { - - for (GlyphID glyphID : fontStackToGlyphIDs.second) { - auto sdf = entry->second.glyphSet.getSDFs().find(glyphID); - if (sdf != entry->second.glyphSet.getSDFs().end()) { - auto glyphRect = entry->second.glyphValues.find(glyphID); - // It's possible to have an SDF without a valid position (if the SDF was malformed). We indicate this case with Rect(0,0,0,0). - const Rect rect = glyphRect == entry->second.glyphValues.end() ? Rect(0,0,0,0) : glyphRect->second.rect; - glyphPositions[fontStackToGlyphIDs.first].emplace(std::piecewise_construct, - std::forward_as_tuple(glyphID), - std::forward_as_tuple(rect, sdf->second.metrics)); - } - } - } - } - return glyphPositions; -} - -void GlyphAtlas::addGlyphs(GlyphRequestor& requestor, const GlyphDependencies &glyphDependencies) { - for (const auto& fontStackToGlyphIDs : glyphDependencies) { - const auto& sdfs = getGlyphSet(fontStackToGlyphIDs.first).getSDFs(); - for (auto glyphID : fontStackToGlyphIDs.second) { + for (const auto& dependency : glyphDependencies) { + const FontStack& fontStack = dependency.first; + const GlyphIDs& glyphIDs = dependency.second; + + GlyphPositions& positions = glyphPositions[fontStack]; + Entry& entry = entries[fontStack]; + const auto& sdfs = entry.glyphSet.getSDFs(); + + for (const auto& glyphID : glyphIDs) { auto it = sdfs.find(glyphID); - if (it != sdfs.end()) { // If we got the range, but still didn't get a glyph, go ahead with rendering - addGlyph(requestor, fontStackToGlyphIDs.first, it->second); - } + if (it == sdfs.end()) + continue; + + addGlyph(requestor, fontStack, it->second); + + // It's possible to have an SDF without a valid position (if the SDF was malformed). + // We indicate this case with Rect(0,0,0,0). + auto glyphRect = entry.glyphValues.find(glyphID); + const Rect rect = glyphRect == entry.glyphValues.end() + ? Rect(0,0,0,0) + : glyphRect->second.rect; + + positions.emplace(std::piecewise_construct, + std::forward_as_tuple(glyphID), + std::forward_as_tuple(rect, it->second.metrics)); } } + + requestor.onGlyphsAvailable(glyphPositions); } void GlyphAtlas::addGlyph(GlyphRequestor& requestor, diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp index a2dce2f5a73..27457bf18c0 100644 --- a/src/mbgl/text/glyph_atlas.hpp +++ b/src/mbgl/text/glyph_atlas.hpp @@ -46,6 +46,9 @@ class GlyphAtlas : public util::noncopyable, public GlyphAtlasObserver { // locally available, the observer will be notified that the glyphs are available // immediately. Otherwise, a request on the FileSource is made, and when all glyphs // are parsed and added to the atlas, the observer will be notified. + // Workers are given a copied 'GlyphPositions' map to use for placing their glyphs. + // The positions specified in this object are guaranteed to be + // valid for the lifetime of the tile. void getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphs); void setURL(const std::string &url) { @@ -73,13 +76,10 @@ class GlyphAtlas : public util::noncopyable, public GlyphAtlasObserver { virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr); friend class ::GlyphAtlasTest; + private: void addGlyphs(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies); - // Workers are given a copied 'GlyphPositions' map to use for placing their glyphs. - // The positions specified in this object are guaranteed to be - // valid for the lifetime of the tile. - GlyphPositionMap getGlyphPositions(const GlyphDependencies& glyphs) const; - + // Only used by GlyphAtlasTest bool hasGlyphRanges(const FontStack&, const GlyphRangeSet& ranges) const; bool hasGlyphRange(const FontStack&, const GlyphRange& range) const; @@ -103,15 +103,6 @@ class GlyphAtlas : public util::noncopyable, public GlyphAtlasObserver { }; std::unordered_map entries; - - struct TileDependency { - TileDependency(const GlyphRangeDependencies& _pendingRanges, GlyphDependencies _glyphDependencies) - : pendingRanges(_pendingRanges), glyphDependencies(std::move(_glyphDependencies)) - {} - GlyphRangeDependencies pendingRanges; - GlyphDependencies glyphDependencies; - }; - std::unordered_map tileDependencies; GlyphAtlasObserver* observer = nullptr; diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp index 6f0a12ea83a..26eff812b76 100644 --- a/src/mbgl/text/glyph_pbf.cpp +++ b/src/mbgl/text/glyph_pbf.cpp @@ -132,12 +132,4 @@ GlyphPBF::GlyphPBF(GlyphAtlas* atlas, GlyphPBF::~GlyphPBF() = default; -void GlyphPBF::addRequestor(GlyphRequestor& requestor) { - requestors.insert(&requestor); -} - -std::set GlyphPBF::processRequestors() { - return std::move(requestors); -} - } // namespace mbgl diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp index 7b524e8ee70..d5b89cd1078 100644 --- a/src/mbgl/text/glyph_pbf.hpp +++ b/src/mbgl/text/glyph_pbf.hpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace mbgl { @@ -26,18 +27,14 @@ class GlyphPBF : private util::noncopyable { FileSource&); ~GlyphPBF(); - void addRequestor(GlyphRequestor&); - std::set processRequestors(); - bool isParsed() const { return parsed; } -private: bool parsed; std::unique_ptr req; GlyphAtlasObserver* observer = nullptr; - std::set requestors; + std::unordered_map> requestors; }; } // namespace mbgl