diff options
Diffstat (limited to 'src/mbgl')
361 files changed, 12068 insertions, 10346 deletions
diff --git a/src/mbgl/actor/actor.hpp b/src/mbgl/actor/actor.hpp deleted file mode 100644 index 810114c513..0000000000 --- a/src/mbgl/actor/actor.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include <mbgl/actor/mailbox.hpp> -#include <mbgl/actor/message.hpp> -#include <mbgl/actor/actor_ref.hpp> -#include <mbgl/util/noncopyable.hpp> - -#include <memory> - -namespace mbgl { - -/* - An `Actor<O>` is an owning reference to an asynchronous object of type `O`: an "actor". - Communication with an actor happens via message passing: you send a message to the object - (using `invoke`), passing a pointer to the member function to call and arguments which - are then forwarded to the actor. - - The actor receives messages sent to it asynchronously, in a manner defined its `Scheduler`. - To store incoming messages before their receipt, each actor has a `Mailbox`, which acts as - a FIFO queue. Messages sent from actor S to actor R are guaranteed to be processed in the - order sent. However, relative order of messages sent by two *different* actors S1 and S2 - to R is *not* guaranteed (and can't be: S1 and S2 may be acting asynchronously with respect - to each other). - - An `Actor<O>` can be converted to an `ActorRef<O>`, a non-owning value object representing - a (weak) reference to the actor. Messages can be sent via the `Ref` as well. - - It's safe -- and encouraged -- to pass `Ref`s between actors via messages. This is how two-way - communication and other forms of collaboration between multiple actors is accomplished. - - It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend - the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is - a no-op. (In the future, a dead-letters queue or log may be implemented.) - - Construction and destruction of an actor is currently synchronous: the corresponding `O` - object is constructed synchronously by the `Actor` constructor, and destructed synchronously - by the `~Actor` destructor, after ensuring that the `O` is not currently receiving an - asynchronous message. (Construction and destruction may change to be asynchronous in the - future.) The constructor of `O` is passed an `ActorRef<O>` referring to itself (which it - can use to self-send messages), followed by the forwarded arguments passed to `Actor<O>`. - - Please don't send messages that contain shared pointers or references. That subverts the - purpose of the actor model: prohibiting direct concurrent access to shared state. -*/ - -template <class Object> -class Actor : public util::noncopyable { -public: - template <class... Args> - Actor(Scheduler& scheduler, Args&&... args_) - : mailbox(std::make_shared<Mailbox>(scheduler)), - object(self(), std::forward<Args>(args_)...) { - } - - ~Actor() { - mailbox->close(); - } - - template <typename Fn, class... Args> - void invoke(Fn fn, Args&&... args) { - mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...)); - } - - ActorRef<std::decay_t<Object>> self() { - return ActorRef<std::decay_t<Object>>(object, mailbox); - } - - operator ActorRef<std::decay_t<Object>>() { - return self(); - } - -private: - std::shared_ptr<Mailbox> mailbox; - Object object; -}; - -} // namespace mbgl diff --git a/src/mbgl/actor/actor_ref.hpp b/src/mbgl/actor/actor_ref.hpp deleted file mode 100644 index 9d858d823f..0000000000 --- a/src/mbgl/actor/actor_ref.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include <mbgl/actor/mailbox.hpp> -#include <mbgl/actor/message.hpp> - -#include <memory> - -namespace mbgl { - -/* - An `ActorRef<O>` is a *non*-owning, weak reference to an actor of type `O`. You can send it - messages just like an `Actor<O>`. It's a value object: safe to copy and pass between actors - via messages. - - An `ActorRef<O>` does not extend the lifetime of the corresponding `Actor<O>`. That's determined - entirely by whichever object owns the `Actor<O>` -- the actor's "supervisor". - - It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend - the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is - a no-op. (In the future, a dead-letters queue or log may be implemented.) -*/ - -template <class Object> -class ActorRef { -public: - ActorRef(Object& object_, std::weak_ptr<Mailbox> weakMailbox_) - : object(object_), - weakMailbox(std::move(weakMailbox_)) { - } - - template <typename Fn, class... Args> - void invoke(Fn fn, Args&&... args) { - if (auto mailbox = weakMailbox.lock()) { - mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...)); - } - } - -private: - Object& object; - std::weak_ptr<Mailbox> weakMailbox; -}; - -} // namespace mbgl diff --git a/src/mbgl/actor/mailbox.cpp b/src/mbgl/actor/mailbox.cpp index 5f60629833..373c24275f 100644 --- a/src/mbgl/actor/mailbox.cpp +++ b/src/mbgl/actor/mailbox.cpp @@ -10,8 +10,24 @@ Mailbox::Mailbox(Scheduler& scheduler_) : scheduler(scheduler_) { } +void Mailbox::close() { + // Block until neither receive() nor push() are in progress. Two mutexes are used because receive() + // must not block send(). Of the two, the receiving mutex must be acquired first, because that is + // the order that an actor will obtain them when it self-sends a message, and consistent lock + // acquisition order prevents deadlocks. + // The receiving mutex is recursive to allow a mailbox (and thus the actor) to close itself. + std::lock_guard<std::recursive_mutex> receivingLock(receivingMutex); + std::lock_guard<std::mutex> pushingLock(pushingMutex); + + closed = true; +} + void Mailbox::push(std::unique_ptr<Message> message) { - assert(!closing); + std::lock_guard<std::mutex> pushingLock(pushingMutex); + + if (closed) { + return; + } std::lock_guard<std::mutex> queueLock(queueMutex); bool wasEmpty = queue.empty(); @@ -21,16 +37,10 @@ void Mailbox::push(std::unique_ptr<Message> message) { } } -void Mailbox::close() { - // Block until the scheduler is guaranteed not to be executing receive(). - std::lock_guard<std::mutex> closingLock(closingMutex); - closing = true; -} - void Mailbox::receive() { - std::lock_guard<std::mutex> closingLock(closingMutex); + std::lock_guard<std::recursive_mutex> receivingLock(receivingMutex); - if (closing) { + if (closed) { return; } diff --git a/src/mbgl/actor/message.hpp b/src/mbgl/actor/message.hpp deleted file mode 100644 index cf071d4933..0000000000 --- a/src/mbgl/actor/message.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include <utility> - -namespace mbgl { - -// A movable type-erasing function wrapper. This allows to store arbitrary invokable -// things (like std::function<>, or the result of a movable-only std::bind()) in the queue. -// Source: http://stackoverflow.com/a/29642072/331379 -class Message { -public: - virtual ~Message() = default; - virtual void operator()() = 0; -}; - -template <class Object, class MemberFn, class ArgsTuple> -class MessageImpl : public Message { -public: - MessageImpl(Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) - : object(object_), - memberFn(memberFn_), - argsTuple(std::move(argsTuple_)) { - } - - void operator()() override { - invoke(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()); - } - - template <std::size_t... I> - void invoke(std::index_sequence<I...>) { - (object.*memberFn)(std::move(std::get<I>(argsTuple))...); - } - - Object& object; - MemberFn memberFn; - ArgsTuple argsTuple; -}; - -namespace actor { - -template <class Object, class MemberFn, class... Args> -std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&... args) { - auto tuple = std::make_tuple(std::forward<Args>(args)...); - return std::make_unique<MessageImpl<Object, MemberFn, decltype(tuple)>>(object, memberFn, std::move(tuple)); -} - -} // namespace actor -} // namespace mbgl diff --git a/src/mbgl/actor/scheduler.cpp b/src/mbgl/actor/scheduler.cpp new file mode 100644 index 0000000000..d7cdb2737b --- /dev/null +++ b/src/mbgl/actor/scheduler.cpp @@ -0,0 +1,19 @@ +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/util/thread_local.hpp> + +namespace mbgl { + +static auto& current() { + static util::ThreadLocal<Scheduler> scheduler; + return scheduler; +}; + +void Scheduler::SetCurrent(Scheduler* scheduler) { + current().set(scheduler); +} + +Scheduler* Scheduler::GetCurrent() { + return current().get(); +} + +} //namespace mbgl diff --git a/src/mbgl/algorithm/generate_clip_ids.cpp b/src/mbgl/algorithm/generate_clip_ids.cpp index 74e0ee242f..287d2a408e 100644 --- a/src/mbgl/algorithm/generate_clip_ids.cpp +++ b/src/mbgl/algorithm/generate_clip_ids.cpp @@ -31,14 +31,14 @@ bool ClipIDGenerator::Leaf::operator==(const Leaf& other) const { return children == other.children; } -std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const { - std::map<UnwrappedTileID, ClipID> stencils; +std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getClipIDs() const { + std::map<UnwrappedTileID, ClipID> clipIDs; // Merge everything. for (auto& pair : pool) { auto& id = pair.first; auto& leaf = pair.second; - auto res = stencils.emplace(id, leaf.clip); + auto res = clipIDs.emplace(id, leaf.clip); if (!res.second) { // Merge with the existing ClipID when there was already an element with the // same tile ID. @@ -46,14 +46,14 @@ std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const { } } - for (auto it = stencils.begin(); it != stencils.end(); ++it) { + for (auto it = clipIDs.begin(); it != clipIDs.end(); ++it) { auto& childId = it->first; auto& childClip = it->second; // Loop through all preceding stencils, and find all parents. for (auto parentIt = std::reverse_iterator<decltype(it)>(it); - parentIt != stencils.rend(); ++parentIt) { + parentIt != clipIDs.rend(); ++parentIt) { auto& parentId = parentIt->first; if (childId.isChildOf(parentId)) { // Once we have a parent, we add the bits that this ID hasn't set yet. @@ -66,11 +66,11 @@ std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const { } // Remove tiles that are entirely covered by children. - util::erase_if(stencils, [&](const auto& stencil) { - return algorithm::coveredByChildren(stencil.first, stencils); + util::erase_if(clipIDs, [&](const auto& stencil) { + return algorithm::coveredByChildren(stencil.first, clipIDs); }); - return stencils; + return clipIDs; } } // namespace algorithm diff --git a/src/mbgl/algorithm/generate_clip_ids.hpp b/src/mbgl/algorithm/generate_clip_ids.hpp index d917b398af..adcf87a72a 100644 --- a/src/mbgl/algorithm/generate_clip_ids.hpp +++ b/src/mbgl/algorithm/generate_clip_ids.hpp @@ -3,9 +3,9 @@ #include <mbgl/tile/tile_id.hpp> #include <mbgl/util/clip_id.hpp> -#include <unordered_set> +#include <set> #include <vector> -#include <unordered_map> +#include <map> namespace mbgl { namespace algorithm { @@ -17,18 +17,18 @@ private: void add(const CanonicalTileID &p); bool operator==(const Leaf &other) const; - std::unordered_set<CanonicalTileID> children; + std::set<CanonicalTileID> children; ClipID& clip; }; uint8_t bit_offset = 0; - std::unordered_multimap<UnwrappedTileID, Leaf> pool; + std::multimap<UnwrappedTileID, Leaf> pool; public: - template <typename Renderables> - void update(Renderables& renderables); + template <typename Renderable> + void update(std::vector<std::reference_wrapper<Renderable>> renderables); - std::map<UnwrappedTileID, ClipID> getStencils() const; + std::map<UnwrappedTileID, ClipID> getClipIDs() const; }; } // namespace algorithm diff --git a/src/mbgl/algorithm/generate_clip_ids_impl.hpp b/src/mbgl/algorithm/generate_clip_ids_impl.hpp index d63ba27b6b..db62214220 100644 --- a/src/mbgl/algorithm/generate_clip_ids_impl.hpp +++ b/src/mbgl/algorithm/generate_clip_ids_impl.hpp @@ -7,14 +7,16 @@ namespace mbgl { namespace algorithm { -template <typename Renderables> -void ClipIDGenerator::update(Renderables& renderables) { +template <typename Renderable> +void ClipIDGenerator::update(std::vector<std::reference_wrapper<Renderable>> renderables) { std::size_t size = 0; + std::sort(renderables.begin(), renderables.end(), + [](const auto& a, const auto& b) { return a.get().id < b.get().id; }); + const auto end = renderables.end(); for (auto it = renderables.begin(); it != end; it++) { - auto& tileID = it->first; - auto& renderable = it->second; + auto& renderable = it->get(); if (!renderable.used) { continue; } @@ -28,17 +30,17 @@ void ClipIDGenerator::update(Renderables& renderables) { // can never be children of the current wrap. auto child_it = std::next(it); const auto children_end = std::lower_bound( - child_it, end, UnwrappedTileID{ static_cast<int16_t>(tileID.wrap + 1), { 0, 0, 0 } }, - [](auto& a, auto& b) { return a.first < b; }); + child_it, end, UnwrappedTileID{ static_cast<int16_t>(renderable.id.wrap + 1), { 0, 0, 0 } }, + [](auto& a, auto& b) { return a.get().id < b; }); for (; child_it != children_end; ++child_it) { - auto& childTileID = child_it->first; - if (childTileID.isChildOf(tileID)) { + auto& childTileID = child_it->get().id; + if (childTileID.isChildOf(it->get().id)) { leaf.add(childTileID.canonical); } } // Find a leaf with matching children. - for (auto its = pool.equal_range(tileID); its.first != its.second; ++its.first) { + for (auto its = pool.equal_range(renderable.id); its.first != its.second; ++its.first) { auto& existing = its.first->second; if (existing == leaf) { leaf.clip = existing.clip; @@ -50,7 +52,7 @@ void ClipIDGenerator::update(Renderables& renderables) { size++; } - pool.emplace(tileID, std::move(leaf)); + pool.emplace(renderable.id, std::move(leaf)); } if (size > 0) { @@ -60,8 +62,8 @@ void ClipIDGenerator::update(Renderables& renderables) { // We are starting our count with 1 since we need at least 1 bit set to distinguish between // areas without any tiles whatsoever and the current area. uint8_t count = 1; - for (auto& pair : renderables) { - auto& renderable = pair.second; + for (auto& it : renderables) { + auto& renderable = it.get(); if (!renderable.used) { continue; } diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp index a9c348b538..0c2266ff47 100644 --- a/src/mbgl/algorithm/update_renderables.hpp +++ b/src/mbgl/algorithm/update_renderables.hpp @@ -31,7 +31,7 @@ void updateRenderables(GetTileFn getTile, assert(idealRenderTileID.canonical.z <= zoomRange.max); assert(dataTileZoom >= idealRenderTileID.canonical.z); - const OverscaledTileID idealDataTileID(dataTileZoom, idealRenderTileID.canonical); + const OverscaledTileID idealDataTileID(dataTileZoom, idealRenderTileID.wrap, idealRenderTileID.canonical); auto tile = getTile(idealDataTileID); if (!tile) { tile = createTile(idealDataTileID); @@ -64,11 +64,11 @@ void updateRenderables(GetTileFn getTile, } else { // Check all four actual child tiles. for (const auto& childTileID : idealDataTileID.canonical.children()) { - const OverscaledTileID childDataTileID(overscaledZ, childTileID); + const OverscaledTileID childDataTileID(overscaledZ, idealRenderTileID.wrap, childTileID); tile = getTile(childDataTileID); if (tile && tile->isRenderable()) { retainTile(*tile, Resource::Necessity::Optional); - renderTile(childDataTileID.unwrapTo(idealRenderTileID.wrap), *tile); + renderTile(childDataTileID.toUnwrapped(), *tile); } else { // At least one child tile doesn't exist, so we are going to look for // parents as well. @@ -81,8 +81,7 @@ void updateRenderables(GetTileFn getTile, // We couldn't find child tiles that entirely cover the ideal tile. for (overscaledZ = dataTileZoom - 1; overscaledZ >= zoomRange.min; --overscaledZ) { const auto parentDataTileID = idealDataTileID.scaledTo(overscaledZ); - const auto parentRenderTileID = - parentDataTileID.unwrapTo(idealRenderTileID.wrap); + const auto parentRenderTileID = parentDataTileID.toUnwrapped(); if (checked.find(parentRenderTileID) != checked.end()) { // Break parent tile ascent, this route has been checked by another child diff --git a/src/mbgl/algorithm/update_tile_masks.hpp b/src/mbgl/algorithm/update_tile_masks.hpp new file mode 100644 index 0000000000..a7840cd163 --- /dev/null +++ b/src/mbgl/algorithm/update_tile_masks.hpp @@ -0,0 +1,128 @@ +#pragma once + +#include <mbgl/renderer/tile_mask.hpp> + +#include <vector> +#include <functional> +#include <algorithm> + +namespace mbgl { +namespace algorithm { + +namespace { + +template <typename Renderable> +void computeTileMasks( + const CanonicalTileID& root, + const UnwrappedTileID ref, + typename std::vector<std::reference_wrapper<Renderable>>::const_iterator it, + const typename std::vector<std::reference_wrapper<Renderable>>::const_iterator end, + TileMask& mask) { + // If the reference or any of its children is found in the list, we need to recurse. + for (; it != end; ++it) { + auto& renderable = it->get(); + if (!renderable.used) { + continue; + } + if (ref == renderable.id) { + // The current tile is masked out, so we don't need to add them to the mask set. + return; + } else if (renderable.id.isChildOf(ref)) { + // There's at least one child tile that is masked out, so recursively descend. + for (const auto& child : ref.children()) { + computeTileMasks<Renderable>(root, child, it, end, mask); + } + return; + } + } + + // We couldn't find a child, so it's definitely a masked part. + // Compute the difference between the root tile ID and the reference tile ID, since TileMask + // elements are always relative (see below for explanation). + const uint8_t diffZ = ref.canonical.z - root.z; + mask.emplace(diffZ, ref.canonical.x - (root.x << diffZ), ref.canonical.y - (root.y << diffZ)); +} + +} // namespace + +// Updates the TileMasks for all renderables. Renderables are objects that have an UnwrappedTileID +// property indicating where they should be rendered on the screen. A TileMask describes all regions +// within that tile that are *not* covered by other Renderables. +// Example: Renderables in our list are 2/1/3, 3/3/6, and 4/5/13. The schematic for creating the +// TileMask for 2/1/3 looks like this: +// +// ┌────────┬────────┬─────────────────┐ +// │ │ │#################│ +// │ 4/4/12 │ 4/5/12 │#################│ +// │ │ │#################│ +// ├──────3/2/6──────┤#####3/3/6#######│ +// │ │########│#################│ +// │ 4/4/13 │#4/5/13#│#################│ +// │ │########│#################│ +// ├────────┴──────2/1/3───────────────┤ +// │ │ │ +// │ │ │ +// │ │ │ +// │ 3/2/7 │ 3/3/7 │ +// │ │ │ +// │ │ │ +// │ │ │ +// └─────────────────┴─────────────────┘ +// +// The TileMask for 2/1/3 thus consists of the tiles 4/4/12, 4/5/12, 4/4/13, 3/2/7, and 3/3/7, +// but it does *not* include 4/5/13, and 3/3/6, since these are other Renderables. +// A TileMask always contains TileIDs *relative* to the tile it is generated for, so 2/1/3 is +// "subtracted" from these TileIDs. The final TileMask for 2/1/3 will thus be: +// +// ┌────────┬────────┬─────────────────┐ +// │ │ │#################│ +// │ 2/0/0 │ 2/1/0 │#################│ +// │ │ │#################│ +// ├────────┼────────┤#################│ +// │ │########│#################│ +// │ 2/0/1 │########│#################│ +// │ │########│#################│ +// ├────────┴────────┼─────────────────┤ +// │ │ │ +// │ │ │ +// │ │ │ +// │ 1/0/1 │ 1/1/1 │ +// │ │ │ +// │ │ │ +// │ │ │ +// └─────────────────┴─────────────────┘ +// +// Only other Renderables that are *children* of the Renderable we are generating the mask for will +// be considered. For example, adding a Renderable with TileID 4/8/13 won't affect the TileMask for +// 2/1/3, since it is not a descendant of it. +template <typename Renderable> +void updateTileMasks(std::vector<std::reference_wrapper<Renderable>> renderables) { + std::sort(renderables.begin(), renderables.end(), + [](const Renderable& a, const Renderable& b) { return a.id < b.id; }); + + TileMask mask; + const auto end = renderables.end(); + for (auto it = renderables.begin(); it != end; it++) { + auto& renderable = it->get(); + if (!renderable.used) { + continue; + } + // Try to add all remaining ids as children. We sorted the tile list + // by z earlier, so all preceding items cannot be children of the current + // tile. We also compute the lower bound of the next wrap, because items of the next wrap + // can never be children of the current wrap. + auto child_it = std::next(it); + const auto children_end = std::lower_bound( + child_it, end, + UnwrappedTileID{ static_cast<int16_t>(renderable.id.wrap + 1), { 0, 0, 0 } }, + [](auto& a, auto& b) { return a.get().id < b; }); + + mask.clear(); + computeTileMasks<Renderable>(renderable.id.canonical, renderable.id, child_it, children_end, + mask); + renderable.setMask(std::move(mask)); + } +} + +} // namespace algorithm +} // namespace mbgl diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index 88153f5fb7..1f2d01e9eb 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -4,8 +4,8 @@ #include <mbgl/annotation/symbol_annotation_impl.hpp> #include <mbgl/annotation/line_annotation_impl.hpp> #include <mbgl/annotation/fill_annotation_impl.hpp> -#include <mbgl/annotation/style_sourced_annotation_impl.hpp> #include <mbgl/style/style.hpp> +#include <mbgl/style/style_impl.hpp> #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/storage/file_source.hpp> @@ -19,39 +19,42 @@ using namespace style; const std::string AnnotationManager::SourceID = "com.mapbox.annotations"; const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; -AnnotationManager::AnnotationManager(float pixelRatio) - : spriteAtlas({ 1024, 1024 }, pixelRatio) { - // This is a special atlas, holding only images added via addIcon, so we always treat it as - // loaded. - spriteAtlas.markAsLoaded(); -} +AnnotationManager::AnnotationManager(Style& style_) + : style(style_) { +}; AnnotationManager::~AnnotationManager() = default; +void AnnotationManager::setStyle(Style& style_) { + style = style_; +} + +void AnnotationManager::onStyleLoaded() { + updateStyle(); +} + AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, const uint8_t maxZoom) { + std::lock_guard<std::mutex> lock(mutex); AnnotationID id = nextID++; Annotation::visit(annotation, [&] (const auto& annotation_) { this->add(id, annotation_, maxZoom); }); + dirty = true; return id; } -Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) { - return Annotation::visit(annotation, [&] (const auto& annotation_) { - return this->update(id, annotation_, maxZoom); +bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) { + std::lock_guard<std::mutex> lock(mutex); + Annotation::visit(annotation, [&] (const auto& annotation_) { + this->update(id, annotation_, maxZoom); }); + return dirty; } void AnnotationManager::removeAnnotation(const AnnotationID& id) { - if (symbolAnnotations.find(id) != symbolAnnotations.end()) { - symbolTree.remove(symbolAnnotations.at(id)); - symbolAnnotations.erase(id); - } else if (shapeAnnotations.find(id) != shapeAnnotations.end()) { - obsoleteShapeAnnotationLayers.insert(shapeAnnotations.at(id)->layerID); - shapeAnnotations.erase(id); - } else { - assert(false); // Should never happen - } + std::lock_guard<std::mutex> lock(mutex); + remove(id); + dirty = true; } void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t) { @@ -63,82 +66,67 @@ void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& anno void AnnotationManager::add(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) { ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id, std::make_unique<LineAnnotationImpl>(id, annotation, maxZoom)).first->second; - obsoleteShapeAnnotationLayers.erase(impl.layerID); + impl.updateStyle(*style.get().impl); } void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) { ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id, std::make_unique<FillAnnotationImpl>(id, annotation, maxZoom)).first->second; - obsoleteShapeAnnotationLayers.erase(impl.layerID); -} - -void AnnotationManager::add(const AnnotationID& id, const StyleSourcedAnnotation& annotation, const uint8_t maxZoom) { - ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id, - std::make_unique<StyleSourcedAnnotationImpl>(id, annotation, maxZoom)).first->second; - obsoleteShapeAnnotationLayers.erase(impl.layerID); + impl.updateStyle(*style.get().impl); } -Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) { - Update result = Update::Nothing; - +void AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) { auto it = symbolAnnotations.find(id); if (it == symbolAnnotations.end()) { assert(false); // Attempt to update a non-existent symbol annotation - return result; + return; } const SymbolAnnotation& existing = it->second->annotation; - if (existing.geometry != annotation.geometry) { - result |= Update::AnnotationData; - } - - if (existing.icon != annotation.icon) { - result |= Update::AnnotationData | Update::AnnotationStyle; - } + if (existing.geometry != annotation.geometry || existing.icon != annotation.icon) { + dirty = true; - if (result != Update::Nothing) { - removeAndAdd(id, annotation, maxZoom); + remove(id); + add(id, annotation, maxZoom); } - - return result; } -Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) { +void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) { auto it = shapeAnnotations.find(id); if (it == shapeAnnotations.end()) { assert(false); // Attempt to update a non-existent shape annotation - return Update::Nothing; + return; } - removeAndAdd(id, annotation, maxZoom); - return Update::AnnotationData | Update::AnnotationStyle; -} -Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) { - auto it = shapeAnnotations.find(id); - if (it == shapeAnnotations.end()) { - assert(false); // Attempt to update a non-existent shape annotation - return Update::Nothing; - } - removeAndAdd(id, annotation, maxZoom); - return Update::AnnotationData | Update::AnnotationStyle; + shapeAnnotations.erase(it); + add(id, annotation, maxZoom); + dirty = true; } -Update AnnotationManager::update(const AnnotationID& id, const StyleSourcedAnnotation& annotation, const uint8_t maxZoom) { +void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) { auto it = shapeAnnotations.find(id); if (it == shapeAnnotations.end()) { assert(false); // Attempt to update a non-existent shape annotation - return Update::Nothing; + return; } - removeAndAdd(id, annotation, maxZoom); - return Update::AnnotationData | Update::AnnotationStyle; + + shapeAnnotations.erase(it); + add(id, annotation, maxZoom); + dirty = true; } -void AnnotationManager::removeAndAdd(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) { - removeAnnotation(id); - Annotation::visit(annotation, [&] (const auto& annotation_) { - this->add(id, annotation_, maxZoom); - }); +void AnnotationManager::remove(const AnnotationID& id) { + if (symbolAnnotations.find(id) != symbolAnnotations.end()) { + symbolTree.remove(symbolAnnotations.at(id)); + symbolAnnotations.erase(id); + } else if (shapeAnnotations.find(id) != shapeAnnotations.end()) { + auto it = shapeAnnotations.find(id); + *style.get().impl->removeLayer(it->second->layerID); + shapeAnnotations.erase(it); + } else { + assert(false); // Should never happen + } } std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const CanonicalTileID& tileID) { @@ -147,13 +135,13 @@ std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const Canonic auto tileData = std::make_unique<AnnotationTileData>(); - AnnotationTileLayer& pointLayer = tileData->layers.emplace(PointLayerID, PointLayerID).first->second; + auto pointLayer = tileData->addLayer(PointLayerID); LatLngBounds tileBounds(tileID); symbolTree.query(boost::geometry::index::intersects(tileBounds), boost::make_function_output_iterator([&](const auto& val){ - val->updateLayer(tileID, pointLayer); + val->updateLayer(tileID, *pointLayer); })); for (const auto& shape : shapeAnnotations) { @@ -163,60 +151,87 @@ std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const Canonic return tileData; } -void AnnotationManager::updateStyle(Style& style) { - // Create annotation source, point layer, and point bucket - if (!style.getSource(SourceID)) { - style.addSource(std::make_unique<AnnotationSource>()); +void AnnotationManager::updateStyle() { + // Create annotation source, point layer, and point bucket. We do everything via Style::Impl + // because we don't want annotation mutations to trigger Style::Impl::styleMutated to be set. + if (!style.get().impl->getSource(SourceID)) { + style.get().impl->addSource(std::make_unique<AnnotationSource>()); std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(PointLayerID, SourceID); layer->setSourceLayer(PointLayerID); - layer->setIconImage({"{sprite}"}); + layer->setIconImage({SourceID + ".{sprite}"}); layer->setIconAllowOverlap(true); layer->setIconIgnorePlacement(true); - style.addLayer(std::move(layer)); + style.get().impl->addLayer(std::move(layer)); } + std::lock_guard<std::mutex> lock(mutex); + for (const auto& shape : shapeAnnotations) { - shape.second->updateStyle(style); + shape.second->updateStyle(*style.get().impl); } - for (const auto& layer : obsoleteShapeAnnotationLayers) { - if (style.getLayer(layer)) { - style.removeLayer(layer); - } + for (const auto& image : images) { + // Call addImage even for images we may have previously added, because we must support + // addAnnotationImage being used to update an existing image. Creating a new image is + // relatively cheap, as it copies only the Immutable reference. (We can't keep track + // of which images need to be added because we don't know if the style is the same + // instance as in the last updateStyle call. If it's a new style, we need to add all + // images.) + style.get().impl->addImage(std::make_unique<style::Image>(image.second)); } - - obsoleteShapeAnnotationLayers.clear(); } void AnnotationManager::updateData() { - for (auto& tile : tiles) { - tile->setData(getTileData(tile->id.canonical)); + std::lock_guard<std::mutex> lock(mutex); + if (dirty) { + for (auto& tile : tiles) { + tile->setData(getTileData(tile->id.canonical)); + } + dirty = false; } } void AnnotationManager::addTile(AnnotationTile& tile) { + std::lock_guard<std::mutex> lock(mutex); tiles.insert(&tile); tile.setData(getTileData(tile.id.canonical)); } void AnnotationManager::removeTile(AnnotationTile& tile) { + std::lock_guard<std::mutex> lock(mutex); tiles.erase(&tile); } -void AnnotationManager::addImage(const std::string& id, std::unique_ptr<style::Image> image) { - spriteAtlas.addImage(id, std::move(image)); +// To ensure that annotation images do not collide with images from the style, +// we prefix input image IDs with "com.mapbox.annotations". +static std::string prefixedImageID(const std::string& id) { + return AnnotationManager::SourceID + "." + id; +} + +void AnnotationManager::addImage(std::unique_ptr<style::Image> image) { + std::lock_guard<std::mutex> lock(mutex); + const std::string id = prefixedImageID(image->getID()); + images.erase(id); + auto inserted = images.emplace(id, style::Image(id, image->getImage().clone(), + image->getPixelRatio(), image->isSdf())); + style.get().impl->addImage(std::make_unique<style::Image>(inserted.first->second)); } -void AnnotationManager::removeImage(const std::string& id) { - spriteAtlas.removeImage(id); +void AnnotationManager::removeImage(const std::string& id_) { + std::lock_guard<std::mutex> lock(mutex); + const std::string id = prefixedImageID(id_); + images.erase(id); + style.get().impl->removeImage(id); } -double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id) { - const style::Image* image = spriteAtlas.getImage(id); - return image ? -(image->image.size.height / image->pixelRatio) / 2 : 0; +double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id_) { + std::lock_guard<std::mutex> lock(mutex); + const std::string id = prefixedImageID(id_); + auto it = images.find(id); + return it != images.end() ? -(it->second.getImage().size.height / it->second.getPixelRatio()) / 2 : 0; } } // namespace mbgl diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp index 0ab43bec15..a028a8f1ba 100644 --- a/src/mbgl/annotation/annotation_manager.hpp +++ b/src/mbgl/annotation/annotation_manager.hpp @@ -2,13 +2,14 @@ #include <mbgl/annotation/annotation.hpp> #include <mbgl/annotation/symbol_annotation_impl.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/map/update.hpp> +#include <mbgl/style/image.hpp> #include <mbgl/util/noncopyable.hpp> +#include <mutex> #include <string> #include <vector> #include <unordered_set> +#include <unordered_map> namespace mbgl { @@ -20,24 +21,24 @@ class ShapeAnnotationImpl; namespace style { class Style; -class Image; } // namespace style class AnnotationManager : private util::noncopyable { public: - AnnotationManager(float pixelRatio); + AnnotationManager(style::Style&); ~AnnotationManager(); AnnotationID addAnnotation(const Annotation&, const uint8_t maxZoom); - Update updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom); + bool updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom); void removeAnnotation(const AnnotationID&); - void addImage(const std::string&, std::unique_ptr<style::Image>); + void addImage(std::unique_ptr<style::Image>); void removeImage(const std::string&); double getTopOffsetPixelsForImage(const std::string&); - SpriteAtlas& getSpriteAtlas() { return spriteAtlas; } - void updateStyle(style::Style&); + void setStyle(style::Style&); + void onStyleLoaded(); + void updateData(); void addTile(AnnotationTile&); @@ -50,17 +51,23 @@ private: void add(const AnnotationID&, const SymbolAnnotation&, const uint8_t); void add(const AnnotationID&, const LineAnnotation&, const uint8_t); void add(const AnnotationID&, const FillAnnotation&, const uint8_t); - void add(const AnnotationID&, const StyleSourcedAnnotation&, const uint8_t); - Update update(const AnnotationID&, const SymbolAnnotation&, const uint8_t); - Update update(const AnnotationID&, const LineAnnotation&, const uint8_t); - Update update(const AnnotationID&, const FillAnnotation&, const uint8_t); - Update update(const AnnotationID&, const StyleSourcedAnnotation&, const uint8_t); + void update(const AnnotationID&, const SymbolAnnotation&, const uint8_t); + void update(const AnnotationID&, const LineAnnotation&, const uint8_t); + void update(const AnnotationID&, const FillAnnotation&, const uint8_t); + + void remove(const AnnotationID&); - void removeAndAdd(const AnnotationID&, const Annotation&, const uint8_t); + void updateStyle(); std::unique_ptr<AnnotationTileData> getTileData(const CanonicalTileID&); + std::reference_wrapper<style::Style> style; + + std::mutex mutex; + + bool dirty = false; + AnnotationID nextID = 0; using SymbolAnnotationTree = boost::geometry::index::rtree<std::shared_ptr<const SymbolAnnotationImpl>, boost::geometry::index::rstar<16, 4>>; @@ -68,13 +75,14 @@ private: // <https://github.com/mapbox/mapbox-gl-native/issues/5691> using SymbolAnnotationMap = std::map<AnnotationID, std::shared_ptr<SymbolAnnotationImpl>>; using ShapeAnnotationMap = std::map<AnnotationID, std::unique_ptr<ShapeAnnotationImpl>>; + using ImageMap = std::unordered_map<std::string, style::Image>; SymbolAnnotationTree symbolTree; SymbolAnnotationMap symbolAnnotations; ShapeAnnotationMap shapeAnnotations; - std::unordered_set<std::string> obsoleteShapeAnnotationLayers; + ImageMap images; + std::unordered_set<AnnotationTile*> tiles; - SpriteAtlas spriteAtlas; friend class AnnotationTile; }; diff --git a/src/mbgl/annotation/annotation_source.cpp b/src/mbgl/annotation/annotation_source.cpp index 9956140179..68f36f2d3a 100644 --- a/src/mbgl/annotation/annotation_source.cpp +++ b/src/mbgl/annotation/annotation_source.cpp @@ -1,25 +1,24 @@ #include <mbgl/annotation/annotation_source.hpp> #include <mbgl/annotation/annotation_manager.hpp> -#include <mbgl/annotation/render_annotation_source.hpp> namespace mbgl { using namespace style; AnnotationSource::AnnotationSource() - : Source(SourceType::Annotations, std::make_unique<Impl>(*this)) { + : Source(makeMutable<Impl>()) { } -AnnotationSource::Impl::Impl(Source& base_) - : Source::Impl(SourceType::Annotations, AnnotationManager::SourceID, base_) { +AnnotationSource::Impl::Impl() + : Source::Impl(SourceType::Annotations, AnnotationManager::SourceID) { } -void AnnotationSource::Impl::loadDescription(FileSource&) { +void AnnotationSource::loadDescription(FileSource&) { loaded = true; } -std::unique_ptr<RenderSource> AnnotationSource::Impl::createRenderSource() const { - return std::make_unique<RenderAnnotationSource>(*this); +optional<std::string> AnnotationSource::Impl::getAttribution() const { + return {}; } } // namespace mbgl diff --git a/src/mbgl/annotation/annotation_source.hpp b/src/mbgl/annotation/annotation_source.hpp index 46c9564443..0728f3207e 100644 --- a/src/mbgl/annotation/annotation_source.hpp +++ b/src/mbgl/annotation/annotation_source.hpp @@ -10,14 +10,19 @@ public: AnnotationSource(); class Impl; + const Impl& impl() const; + +private: + void loadDescription(FileSource&) final; + + Mutable<Impl> mutableImpl() const; }; class AnnotationSource::Impl : public style::Source::Impl { public: - Impl(Source&); + Impl(); - void loadDescription(FileSource&) final; - std::unique_ptr<RenderSource> createRenderSource() const final; + optional<std::string> getAttribution() const final; }; } // namespace mbgl diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp index 1253681414..0596d60f4f 100644 --- a/src/mbgl/annotation/annotation_tile.cpp +++ b/src/mbgl/annotation/annotation_tile.cpp @@ -3,7 +3,6 @@ #include <mbgl/util/constants.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/renderer/tile_parameters.hpp> -#include <mbgl/style/style.hpp> #include <utility> @@ -11,9 +10,7 @@ namespace mbgl { AnnotationTile::AnnotationTile(const OverscaledTileID& overscaledTileID, const TileParameters& parameters) - : GeometryTile(overscaledTileID, AnnotationManager::SourceID, parameters, - *parameters.style.glyphAtlas, - parameters.annotationManager.spriteAtlas), + : GeometryTile(overscaledTileID, AnnotationManager::SourceID, parameters), annotationManager(parameters.annotationManager) { annotationManager.addTile(*this); } @@ -22,37 +19,105 @@ AnnotationTile::~AnnotationTile() { annotationManager.removeTile(*this); } -void AnnotationTile::setNecessity(Necessity) {} +void AnnotationTile::setNecessity(Necessity) { +} + +class AnnotationTileFeatureData { +public: + AnnotationTileFeatureData(const AnnotationID id_, + FeatureType type_, + GeometryCollection&& geometries_, + std::unordered_map<std::string, std::string>&& properties_) + : id(id_), + type(type_), + geometries(std::move(geometries_)), + properties(std::move(properties_)) { + } + + AnnotationID id; + FeatureType type; + GeometryCollection geometries; + std::unordered_map<std::string, std::string> properties; +}; -AnnotationTileFeature::AnnotationTileFeature(const AnnotationID id_, - FeatureType type_, GeometryCollection geometries_, - std::unordered_map<std::string, std::string> properties_) - : id(id_), - type(type_), - properties(std::move(properties_)), - geometries(std::move(geometries_)) {} +AnnotationTileFeature::AnnotationTileFeature(std::shared_ptr<const AnnotationTileFeatureData> data_) + : data(std::move(data_)) { +} + +AnnotationTileFeature::~AnnotationTileFeature() = default; + +FeatureType AnnotationTileFeature::getType() const { + return data->type; +} optional<Value> AnnotationTileFeature::getValue(const std::string& key) const { - auto it = properties.find(key); - if (it != properties.end()) { + auto it = data->properties.find(key); + if (it != data->properties.end()) { return optional<Value>(it->second); } return optional<Value>(); } -AnnotationTileLayer::AnnotationTileLayer(std::string name_) - : name(std::move(name_)) {} +optional<FeatureIdentifier> AnnotationTileFeature::getID() const { + return { static_cast<uint64_t>(data->id) }; +} + +GeometryCollection AnnotationTileFeature::getGeometries() const { + return data->geometries; +} + +class AnnotationTileLayerData { +public: + AnnotationTileLayerData(const std::string& name_) : name(name_) { + } + + const std::string name; + std::vector<std::shared_ptr<const AnnotationTileFeatureData>> features; +}; + +AnnotationTileLayer::AnnotationTileLayer(std::shared_ptr<AnnotationTileLayerData> layer_) : layer(std::move(layer_)) { +} + +std::size_t AnnotationTileLayer::featureCount() const { + return layer->features.size(); +} + +std::unique_ptr<GeometryTileFeature> AnnotationTileLayer::getFeature(std::size_t i) const { + return std::make_unique<AnnotationTileFeature>(layer->features.at(i)); +} + +std::string AnnotationTileLayer::getName() const { + return layer->name; +} + +void AnnotationTileLayer::addFeature(const AnnotationID id, + FeatureType type, + GeometryCollection geometries, + std::unordered_map<std::string, std::string> properties) { + + layer->features.emplace_back(std::make_shared<AnnotationTileFeatureData>( + id, type, std::move(geometries), std::move(properties))); +} std::unique_ptr<GeometryTileData> AnnotationTileData::clone() const { return std::make_unique<AnnotationTileData>(*this); } -const GeometryTileLayer* AnnotationTileData::getLayer(const std::string& name) const { +std::unique_ptr<GeometryTileLayer> AnnotationTileData::getLayer(const std::string& name) const { auto it = layers.find(name); if (it != layers.end()) { - return &it->second; + return std::make_unique<AnnotationTileLayer>(it->second); } return nullptr; } +std::unique_ptr<AnnotationTileLayer> AnnotationTileData::addLayer(const std::string& name) { + // Only constructs a new layer if it doesn't yet exist, otherwise, we'll use the existing one. + auto it = layers.find(name); + if (it == layers.end()) { + it = layers.emplace(name, std::make_shared<AnnotationTileLayerData>(name)).first; + } + return std::make_unique<AnnotationTileLayer>(it->second); +} + } // namespace mbgl diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp index ea4ff5ebd5..88505c50e3 100644 --- a/src/mbgl/annotation/annotation_tile.hpp +++ b/src/mbgl/annotation/annotation_tile.hpp @@ -11,8 +11,7 @@ class TileParameters; class AnnotationTile : public GeometryTile { public: - AnnotationTile(const OverscaledTileID&, - const TileParameters&); + AnnotationTile(const OverscaledTileID&, const TileParameters&); ~AnnotationTile() override; void setNecessity(Necessity) final; @@ -21,46 +20,50 @@ private: AnnotationManager& annotationManager; }; +class AnnotationTileFeatureData; + class AnnotationTileFeature : public GeometryTileFeature { public: - AnnotationTileFeature(AnnotationID, FeatureType, GeometryCollection, - std::unordered_map<std::string, std::string> properties = {{}}); + AnnotationTileFeature(std::shared_ptr<const AnnotationTileFeatureData>); + ~AnnotationTileFeature() override; - FeatureType getType() const override { return type; } + FeatureType getType() const override; optional<Value> getValue(const std::string&) const override; - optional<FeatureIdentifier> getID() const override { return { static_cast<uint64_t>(id) }; } - GeometryCollection getGeometries() const override { return geometries; } + optional<FeatureIdentifier> getID() const override; + GeometryCollection getGeometries() const override; - const AnnotationID id; - const FeatureType type; - const std::unordered_map<std::string, std::string> properties; - const GeometryCollection geometries; +private: + std::shared_ptr<const AnnotationTileFeatureData> data; }; +class AnnotationTileLayerData; + class AnnotationTileLayer : public GeometryTileLayer { public: - AnnotationTileLayer(std::string); - - std::size_t featureCount() const override { return features.size(); } + AnnotationTileLayer(std::shared_ptr<AnnotationTileLayerData>); - std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override { - return std::make_unique<AnnotationTileFeature>(features.at(i)); - } + std::size_t featureCount() const override; + std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override; + std::string getName() const override; - std::string getName() const override { return name; }; - - std::vector<AnnotationTileFeature> features; + void addFeature(const AnnotationID, + FeatureType, + GeometryCollection, + std::unordered_map<std::string, std::string> properties = { {} }); private: - std::string name; + std::shared_ptr<AnnotationTileLayerData> layer; }; class AnnotationTileData : public GeometryTileData { public: std::unique_ptr<GeometryTileData> clone() const override; - const GeometryTileLayer* getLayer(const std::string&) const override; + std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override; + + std::unique_ptr<AnnotationTileLayer> addLayer(const std::string&); - std::unordered_map<std::string, AnnotationTileLayer> layers; +private: + std::unordered_map<std::string, std::shared_ptr<AnnotationTileLayerData>> layers; }; } // namespace mbgl diff --git a/src/mbgl/annotation/fill_annotation_impl.cpp b/src/mbgl/annotation/fill_annotation_impl.cpp index 3e91524e86..9c73aeb796 100644 --- a/src/mbgl/annotation/fill_annotation_impl.cpp +++ b/src/mbgl/annotation/fill_annotation_impl.cpp @@ -1,6 +1,6 @@ #include <mbgl/annotation/fill_annotation_impl.hpp> #include <mbgl/annotation/annotation_manager.hpp> -#include <mbgl/style/style.hpp> +#include <mbgl/style/style_impl.hpp> #include <mbgl/style/layers/fill_layer.hpp> namespace mbgl { @@ -9,10 +9,10 @@ using namespace style; FillAnnotationImpl::FillAnnotationImpl(AnnotationID id_, FillAnnotation annotation_, uint8_t maxZoom_) : ShapeAnnotationImpl(id_, maxZoom_), - annotation({ ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.color, annotation_.outlineColor }) { + annotation(ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.color, annotation_.outlineColor) { } -void FillAnnotationImpl::updateStyle(Style& style) const { +void FillAnnotationImpl::updateStyle(Style::Impl& style) const { Layer* layer = style.getLayer(layerID); if (!layer) { @@ -21,7 +21,7 @@ void FillAnnotationImpl::updateStyle(Style& style) const { layer = style.addLayer(std::move(newLayer), AnnotationManager::PointLayerID); } - FillLayer* fillLayer = layer->as<FillLayer>(); + auto* fillLayer = layer->as<FillLayer>(); fillLayer->setFillOpacity(annotation.opacity); fillLayer->setFillColor(annotation.color); fillLayer->setFillOutlineColor(annotation.outlineColor); diff --git a/src/mbgl/annotation/fill_annotation_impl.hpp b/src/mbgl/annotation/fill_annotation_impl.hpp index 6376eee880..5c49e447b8 100644 --- a/src/mbgl/annotation/fill_annotation_impl.hpp +++ b/src/mbgl/annotation/fill_annotation_impl.hpp @@ -9,7 +9,7 @@ class FillAnnotationImpl : public ShapeAnnotationImpl { public: FillAnnotationImpl(AnnotationID, FillAnnotation, uint8_t maxZoom); - void updateStyle(style::Style&) const final; + void updateStyle(style::Style::Impl&) const final; const ShapeAnnotationGeometry& geometry() const final; private: diff --git a/src/mbgl/annotation/line_annotation_impl.cpp b/src/mbgl/annotation/line_annotation_impl.cpp index 15fa2c67f3..d35b956888 100644 --- a/src/mbgl/annotation/line_annotation_impl.cpp +++ b/src/mbgl/annotation/line_annotation_impl.cpp @@ -1,6 +1,6 @@ #include <mbgl/annotation/line_annotation_impl.hpp> #include <mbgl/annotation/annotation_manager.hpp> -#include <mbgl/style/style.hpp> +#include <mbgl/style/style_impl.hpp> #include <mbgl/style/layers/line_layer.hpp> namespace mbgl { @@ -9,10 +9,10 @@ using namespace style; LineAnnotationImpl::LineAnnotationImpl(AnnotationID id_, LineAnnotation annotation_, uint8_t maxZoom_) : ShapeAnnotationImpl(id_, maxZoom_), - annotation({ ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.width, annotation_.color }) { + annotation(ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.width, annotation_.color) { } -void LineAnnotationImpl::updateStyle(Style& style) const { +void LineAnnotationImpl::updateStyle(Style::Impl& style) const { Layer* layer = style.getLayer(layerID); if (!layer) { @@ -22,7 +22,7 @@ void LineAnnotationImpl::updateStyle(Style& style) const { layer = style.addLayer(std::move(newLayer), AnnotationManager::PointLayerID); } - LineLayer* lineLayer = layer->as<LineLayer>(); + auto* lineLayer = layer->as<LineLayer>(); lineLayer->setLineOpacity(annotation.opacity); lineLayer->setLineWidth(annotation.width); lineLayer->setLineColor(annotation.color); diff --git a/src/mbgl/annotation/line_annotation_impl.hpp b/src/mbgl/annotation/line_annotation_impl.hpp index 7945da5d97..548a094d53 100644 --- a/src/mbgl/annotation/line_annotation_impl.hpp +++ b/src/mbgl/annotation/line_annotation_impl.hpp @@ -9,7 +9,7 @@ class LineAnnotationImpl : public ShapeAnnotationImpl { public: LineAnnotationImpl(AnnotationID, LineAnnotation, uint8_t maxZoom); - void updateStyle(style::Style&) const final; + void updateStyle(style::Style::Impl&) const final; const ShapeAnnotationGeometry& geometry() const final; private: diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index a62d2d51d3..34fb576727 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -1,6 +1,7 @@ #include <mbgl/annotation/render_annotation_source.hpp> #include <mbgl/annotation/annotation_tile.hpp> #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/algorithm/generate_clip_ids.hpp> #include <mbgl/algorithm/generate_clip_ids_impl.hpp> @@ -9,65 +10,65 @@ namespace mbgl { using namespace style; -RenderAnnotationSource::RenderAnnotationSource(const AnnotationSource::Impl& impl_) +RenderAnnotationSource::RenderAnnotationSource(Immutable<AnnotationSource::Impl> impl_) : RenderSource(impl_) { tilePyramid.setObserver(this); } +const AnnotationSource::Impl& RenderAnnotationSource::impl() const { + return static_cast<const AnnotationSource::Impl&>(*baseImpl); +} + bool RenderAnnotationSource::isLoaded() const { return tilePyramid.isLoaded(); } -void RenderAnnotationSource::invalidateTiles() { - tilePyramid.invalidateTiles(); +void RenderAnnotationSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters) { + std::swap(baseImpl, baseImpl_); + + enabled = needsRendering; + + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::Annotations, + util::tileSize, + { 0, util::DEFAULT_MAX_ZOOM }, + [&] (const OverscaledTileID& tileID) { + return std::make_unique<AnnotationTile>(tileID, parameters); + }); } -void RenderAnnotationSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { - generator.update(tilePyramid.getRenderTiles()); - tilePyramid.startRender(projMatrix, clipMatrix, transform); +void RenderAnnotationSource::startRender(PaintParameters& parameters) { + parameters.clipIDGenerator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderAnnotationSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); +void RenderAnnotationSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); } -std::map<UnwrappedTileID, RenderTile>& RenderAnnotationSource::getRenderTiles() { +std::vector<std::reference_wrapper<RenderTile>> RenderAnnotationSource::getRenderTiles() { return tilePyramid.getRenderTiles(); } -void RenderAnnotationSource::updateTiles(const TileParameters& parameters) { - tilePyramid.updateTiles(parameters, - SourceType::Annotations, - util::tileSize, - { 0, 22 }, - [&] (const OverscaledTileID& tileID) { - return std::make_unique<AnnotationTile>(tileID, parameters); - }); -} - -void RenderAnnotationSource::removeTiles() { - tilePyramid.removeTiles(); -} - -void RenderAnnotationSource::reloadTiles() { - tilePyramid.reloadTiles(); -} - std::unordered_map<std::string, std::vector<Feature>> RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, options); + const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector<Feature> RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const { return {}; } -void RenderAnnotationSource::setCacheSize(size_t size) { - tilePyramid.setCacheSize(size); -} - void RenderAnnotationSource::onLowMemory() { tilePyramid.onLowMemory(); } diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp index 9ae9340477..9536b2e101 100644 --- a/src/mbgl/annotation/render_annotation_source.hpp +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -8,46 +8,42 @@ namespace mbgl { class RenderAnnotationSource : public RenderSource { public: - RenderAnnotationSource(const AnnotationSource::Impl&); + RenderAnnotationSource(Immutable<AnnotationSource::Impl>); bool isLoaded() const final; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&) final; + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; - // Removes all tiles (by putting them into the cache). - void removeTiles() final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; - // Remove all tiles and clear the cache. - void invalidateTiles() final; - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles() final; - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) final; - void finishRender(Painter&) final; - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final; + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final; std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; private: + const AnnotationSource::Impl& impl() const; + TilePyramid tilePyramid; }; +template <> +inline bool RenderSource::is<RenderAnnotationSource>() const { + return baseImpl->type == SourceType::Annotations; +} + } // namespace mbgl diff --git a/src/mbgl/annotation/shape_annotation_impl.cpp b/src/mbgl/annotation/shape_annotation_impl.cpp index d3ddf62b9e..0c1a631ad8 100644 --- a/src/mbgl/annotation/shape_annotation_impl.cpp +++ b/src/mbgl/annotation/shape_annotation_impl.cpp @@ -38,7 +38,7 @@ void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, Annotati if (shapeTile.features.empty()) return; - AnnotationTileLayer& layer = data.layers.emplace(layerID, layerID).first->second; + auto layer = data.addLayer(layerID); ToGeometryCollection toGeometryCollection; ToFeatureType toFeatureType; @@ -53,7 +53,7 @@ void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, Annotati renderGeometry = fixupPolygons(renderGeometry); } - layer.features.emplace_back(id, featureType, renderGeometry); + layer->addFeature(id, featureType, renderGeometry); } } diff --git a/src/mbgl/annotation/shape_annotation_impl.hpp b/src/mbgl/annotation/shape_annotation_impl.hpp index 800b4ec313..caf2cff1a5 100644 --- a/src/mbgl/annotation/shape_annotation_impl.hpp +++ b/src/mbgl/annotation/shape_annotation_impl.hpp @@ -1,9 +1,11 @@ #pragma once +#include <mbgl/util/string.hpp> #include <mapbox/geojsonvt.hpp> #include <mbgl/annotation/annotation.hpp> #include <mbgl/util/geometry.hpp> +#include <mbgl/style/style.hpp> #include <string> #include <memory> @@ -13,16 +15,12 @@ namespace mbgl { class AnnotationTileData; class CanonicalTileID; -namespace style { -class Style; -} // namespace style - class ShapeAnnotationImpl { public: ShapeAnnotationImpl(const AnnotationID, const uint8_t maxZoom); virtual ~ShapeAnnotationImpl() = default; - virtual void updateStyle(style::Style&) const = 0; + virtual void updateStyle(style::Style::Impl&) const = 0; virtual const ShapeAnnotationGeometry& geometry() const = 0; void updateTileData(const CanonicalTileID&, AnnotationTileData&); diff --git a/src/mbgl/annotation/style_sourced_annotation_impl.cpp b/src/mbgl/annotation/style_sourced_annotation_impl.cpp deleted file mode 100644 index cb664cf15d..0000000000 --- a/src/mbgl/annotation/style_sourced_annotation_impl.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <mbgl/annotation/style_sourced_annotation_impl.hpp> -#include <mbgl/annotation/annotation_manager.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/style/layer.hpp> -#include <mbgl/style/layer_impl.hpp> -#include <mbgl/style/layers/line_layer.hpp> -#include <mbgl/style/layers/fill_layer.hpp> - -namespace mbgl { - -using namespace style; - -StyleSourcedAnnotationImpl::StyleSourcedAnnotationImpl(AnnotationID id_, StyleSourcedAnnotation annotation_, uint8_t maxZoom_) - : ShapeAnnotationImpl(id_, maxZoom_), - annotation(std::move(annotation_)) { -} - -void StyleSourcedAnnotationImpl::updateStyle(Style& style) const { - if (style.getLayer(layerID)) - return; - - const Layer* sourceLayer = style.getLayer(annotation.layerID); - if (!sourceLayer) - return; - - if (sourceLayer->is<LineLayer>()) { - std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, AnnotationManager::SourceID); - layer->as<LineLayer>()->setSourceLayer(layerID); - layer->as<LineLayer>()->setVisibility(VisibilityType::Visible); - style.addLayer(std::move(layer), sourceLayer->getID()); - } else if (sourceLayer->is<FillLayer>()) { - std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, AnnotationManager::SourceID); - layer->as<FillLayer>()->setSourceLayer(layerID); - layer->as<FillLayer>()->setVisibility(VisibilityType::Visible); - style.addLayer(std::move(layer), sourceLayer->getID()); - } -} - -const ShapeAnnotationGeometry& StyleSourcedAnnotationImpl::geometry() const { - return annotation.geometry; -} - -} // namespace mbgl diff --git a/src/mbgl/annotation/style_sourced_annotation_impl.hpp b/src/mbgl/annotation/style_sourced_annotation_impl.hpp deleted file mode 100644 index 82b947302d..0000000000 --- a/src/mbgl/annotation/style_sourced_annotation_impl.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include <mbgl/annotation/shape_annotation_impl.hpp> -#include <mbgl/annotation/annotation.hpp> - -namespace mbgl { - -class StyleSourcedAnnotationImpl : public ShapeAnnotationImpl { -public: - StyleSourcedAnnotationImpl(AnnotationID, StyleSourcedAnnotation, uint8_t maxZoom); - - void updateStyle(style::Style&) const final; - const ShapeAnnotationGeometry& geometry() const final; - -private: - const StyleSourcedAnnotation annotation; -}; - -} // namespace mbgl diff --git a/src/mbgl/annotation/symbol_annotation_impl.cpp b/src/mbgl/annotation/symbol_annotation_impl.cpp index e5ae5f4b91..c95eb481b5 100644 --- a/src/mbgl/annotation/symbol_annotation_impl.cpp +++ b/src/mbgl/annotation/symbol_annotation_impl.cpp @@ -18,7 +18,7 @@ void SymbolAnnotationImpl::updateLayer(const CanonicalTileID& tileID, Annotation LatLng latLng { annotation.geometry.y, annotation.geometry.x }; TileCoordinate coordinate = TileCoordinate::fromLatLng(0, latLng); GeometryCoordinate tilePoint = TileCoordinate::toGeometryCoordinate(UnwrappedTileID(0, tileID), coordinate.p); - layer.features.emplace_back(id, FeatureType::Point, GeometryCollection {{ {{ tilePoint }} }}, featureProperties); + layer.addFeature(id, FeatureType::Point, GeometryCollection {{ {{ tilePoint }} }}, featureProperties); } } // namespace mbgl diff --git a/src/mbgl/annotation/symbol_annotation_impl.hpp b/src/mbgl/annotation/symbol_annotation_impl.hpp index 2c263ab41e..e41ff85f33 100644 --- a/src/mbgl/annotation/symbol_annotation_impl.hpp +++ b/src/mbgl/annotation/symbol_annotation_impl.hpp @@ -29,10 +29,6 @@ #include <boost/geometry/index/rtree.hpp> #pragma GCC diagnostic pop -// Make Boost Geometry aware of our LatLng type -BOOST_GEOMETRY_REGISTER_POINT_2D_CONST(mbgl::LatLng, double, boost::geometry::cs::cartesian, longitude(), latitude()) -BOOST_GEOMETRY_REGISTER_BOX(mbgl::LatLngBounds, mbgl::LatLng, southwest(), northeast()) - namespace mbgl { class AnnotationTileLayer; @@ -50,9 +46,42 @@ public: } // namespace mbgl -// Tell Boost Geometry how to access a std::shared_ptr<mbgl::SymbolAnnotation> object. namespace boost { namespace geometry { + +// Make Boost Geometry aware of our LatLng type +namespace traits { + +template<> struct tag<mbgl::LatLng> { using type = point_tag; }; +template<> struct dimension<mbgl::LatLng> : boost::mpl::int_<2> {}; +template<> struct coordinate_type<mbgl::LatLng> { using type = double; }; +template<> struct coordinate_system<mbgl::LatLng> { using type = boost::geometry::cs::cartesian; }; + +template<> struct access<mbgl::LatLng, 0> { static inline double get(mbgl::LatLng const& p) { return p.longitude(); } }; +template<> struct access<mbgl::LatLng, 1> { static inline double get(mbgl::LatLng const& p) { return p.latitude(); } }; + +template<> struct tag<mbgl::LatLngBounds> { using type = box_tag; }; +template<> struct point_type<mbgl::LatLngBounds> { using type = mbgl::LatLng; }; + +template <size_t D> +struct indexed_access<mbgl::LatLngBounds, min_corner, D> +{ + using ct = coordinate_type<mbgl::LatLng>::type; + static inline ct get(mbgl::LatLngBounds const& b) { return geometry::get<D>(b.southwest()); } + static inline void set(mbgl::LatLngBounds& b, ct const& value) { geometry::set<D>(b.southwest(), value); } +}; + +template <size_t D> +struct indexed_access<mbgl::LatLngBounds, max_corner, D> +{ + using ct = coordinate_type<mbgl::LatLng>::type; + static inline ct get(mbgl::LatLngBounds const& b) { return geometry::get<D>(b.northeast()); } + static inline void set(mbgl::LatLngBounds& b, ct const& value) { geometry::set<D>(b.northeast(), value); } +}; + +} // namespace traits + +// Tell Boost Geometry how to access a std::shared_ptr<mbgl::SymbolAnnotation> object. namespace index { template <> @@ -64,6 +93,7 @@ struct indexable<std::shared_ptr<const mbgl::SymbolAnnotationImpl>> { } }; -} // end namespace index -} // end namespace geometry -} // end namespace boost +} // namespace index + +} // namespace geometry +} // namespace boost diff --git a/src/mbgl/geometry/anchor.hpp b/src/mbgl/geometry/anchor.hpp index 3ed2b23e1b..b24d8d04e0 100644 --- a/src/mbgl/geometry/anchor.hpp +++ b/src/mbgl/geometry/anchor.hpp @@ -17,6 +17,6 @@ public: : point(x_, y_), angle(angle_), scale(scale_), segment(segment_) {} }; -typedef std::vector<Anchor> Anchors; +using Anchors = std::vector<Anchor>; } // namespace mbgl diff --git a/src/mbgl/geometry/binpack.hpp b/src/mbgl/geometry/binpack.hpp deleted file mode 100644 index b715cbc2be..0000000000 --- a/src/mbgl/geometry/binpack.hpp +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/rect.hpp> -#include <cstdint> -#include <list> - -namespace mbgl { - -template <typename T> -class BinPack : private util::noncopyable { -public: - BinPack(T width, T height) - : free(1, Rect<uint16_t>{ 0, 0, width, height }) {} -public: - Rect<T> allocate(T width, T height) { - // Find the smallest free rect angle - auto smallest = free.end(); - for (auto it = free.begin(); it != free.end(); ++it) { - const Rect<T>& ref = *it; - const Rect<T>& rect = *smallest; - if (width <= ref.w && height <= ref.h) { - if (smallest == free.end() || (ref.y <= rect.y && ref.x <= rect.x)) { - smallest = it; - } else { - // Our current "smallest" rect is already closer to 0/0. - } - } else { - // The rect in the free list is not big enough. - } - } - - if (smallest == free.end()) { - // There's no space left for this char. - return Rect<uint16_t>{ 0, 0, 0, 0 }; - } else { - Rect<T> rect = *smallest; - free.erase(smallest); - - // Shorter/Longer Axis Split Rule (SAS) - // http://clb.demon.fi/files/RectangleBinPack.pdf p. 15 - // Ignore the dimension of R and just split long the shorter dimension - // See Also: http://www.cs.princeton.edu/~chazelle/pubs/blbinpacking.pdf - if (rect.w < rect.h) { - // split horizontally - // +--+---+ - // |__|___| <-- b1 - // +------+ <-- b2 - if (rect.w > width) free.emplace_back(rect.x + width, rect.y, rect.w - width, height); - if (rect.h > height) free.emplace_back(rect.x, rect.y + height, rect.w, rect.h - height); - } else { - // split vertically - // +--+---+ - // |__| | <-- b1 - // +--|---+ <-- b2 - if (rect.w > width) free.emplace_back(rect.x + width, rect.y, rect.w - width, rect.h); - if (rect.h > height) free.emplace_back(rect.x, rect.y + height, width, rect.h - height); - } - - return Rect<uint16_t>{ rect.x, rect.y, width, height }; - } - } - - - void release(Rect<T> rect) { - // Simple algorithm to recursively merge the newly released cell with its - // neighbor. This doesn't merge more than two cells at a time, and fails - // for complicated merges. - for (auto it = free.begin(); it != free.end(); ++it) { - Rect<T> ref = *it; - if (ref.y == rect.y && ref.h == rect.h && ref.x + ref.w == rect.x) { - ref.w += rect.w; - } - else if (ref.x == rect.x && ref.w == rect.w && ref.y + ref.h == rect.y) { - ref.h += rect.h; - } - else if (rect.y == ref.y && rect.h == ref.h && rect.x + rect.w == ref.x) { - ref.x = rect.x; - ref.w += rect.w; - } - else if (rect.x == ref.x && rect.w == ref.w && rect.y + rect.h == ref.y) { - ref.y = rect.y; - ref.h += rect.h; - } else { - continue; - } - - free.erase(it); - release(ref); - return; - - } - - free.emplace_back(rect); - }; - -private: - std::list<Rect<T>> free; -}; - -} // namespace mbgl diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 8251e4d03a..1adb933e44 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -1,15 +1,14 @@ #include <mbgl/geometry/feature_index.hpp> -#include <mbgl/style/style.hpp> #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> +#include <mbgl/renderer/query.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/text/collision_tile.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/math.hpp> #include <mbgl/math/minmax.hpp> -#include <mbgl/map/query.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/tile/geometry_tile.hpp> +#include <mbgl/tile/tile_id.hpp> #include <mapbox/geometry/envelope.hpp> @@ -32,19 +31,6 @@ void FeatureIndex::insert(const GeometryCollection& geometries, } } -static bool vectorContains(const std::vector<std::string>& vector, const std::string& s) { - return std::find(vector.begin(), vector.end(), s) != vector.end(); -} - -static bool vectorsIntersect(const std::vector<std::string>& vectorA, const std::vector<std::string>& vectorB) { - for (const auto& a : vectorA) { - if (vectorContains(vectorB, a)) { - return true; - } - } - return false; -} - static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) { return a.sortIndex > b.sortIndex; } @@ -53,36 +39,6 @@ static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& return a.sortIndex < b.sortIndex; } -static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions, - const style::Style& style, - const GeometryTile& tile, - const float pixelsToTileUnits) { - - // Determine the additional radius needed factoring in property functions - float additionalRadius = 0; - auto getQueryRadius = [&](const RenderLayer& layer) { - auto bucket = tile.getBucket(layer); - if (bucket) { - additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits); - } - }; - - if (queryOptions.layerIDs) { - for (const auto& layerID : *queryOptions.layerIDs) { - RenderLayer* layer = style.getRenderLayer(layerID); - if (layer) { - getQueryRadius(*layer); - } - } - } else { - for (const RenderLayer* layer : style.getRenderLayers()) { - getQueryRadius(*layer); - } - } - - return std::min<int16_t>(util::EXTENT, additionalRadius); -} - void FeatureIndex::query( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, @@ -92,13 +48,13 @@ void FeatureIndex::query( const RenderedQueryOptions& queryOptions, const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, - const style::Style& style, + const std::vector<const RenderLayer*>& layers, const CollisionTile* collisionTile, - const GeometryTile& tile) const { + const float additionalQueryRadius) const { // Determine query radius const float pixelsToTileUnits = util::EXTENT / tileSize / scale; - const int16_t additionalRadius = getAdditionalQueryRadius(queryOptions, style, tile, pixelsToTileUnits); + const int16_t additionalRadius = std::min<int16_t>(util::EXTENT, additionalQueryRadius * pixelsToTileUnits); // Query the grid index mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry); @@ -113,7 +69,7 @@ void FeatureIndex::query( if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; - addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits); + addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits); } // Query symbol features, if they've been placed. @@ -124,7 +80,7 @@ void FeatureIndex::query( std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale); std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols); for (const auto& symbolFeature : symbolFeatures) { - addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits); + addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits); } } @@ -135,30 +91,39 @@ void FeatureIndex::addFeature( const RenderedQueryOptions& options, const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, - const style::Style& style, + const std::vector<const RenderLayer*>& layers, const float bearing, const float pixelsToTileUnits) const { - auto& layerIDs = bucketLayerIDs.at(indexedFeature.bucketName); - if (options.layerIDs && !vectorsIntersect(layerIDs, *options.layerIDs)) { - return; - } - - auto sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); - assert(sourceLayer); + auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* { + for (const auto& layer : layers) { + if (layer->getID() == layerID) { + return layer; + } + } + return nullptr; + }; - auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); - assert(geometryTileFeature); + // Lazily calculated. + std::unique_ptr<GeometryTileLayer> sourceLayer; + std::unique_ptr<GeometryTileFeature> geometryTileFeature; - for (const auto& layerID : layerIDs) { - if (options.layerIDs && !vectorContains(*options.layerIDs, layerID)) { + for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) { + const RenderLayer* renderLayer = getRenderLayer(layerID); + if (!renderLayer) { continue; } - auto renderLayer = style.getRenderLayer(layerID); - if (!renderLayer || - (!renderLayer->is<RenderSymbolLayer>() && - !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits))) { + if (!geometryTileFeature) { + sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); + assert(sourceLayer); + + geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); + assert(geometryTileFeature); + } + + if (!renderLayer->is<RenderSymbolLayer>() && + !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) { continue; } diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index f7aa0182e4..2ae7da33df 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -11,12 +11,8 @@ namespace mbgl { -class GeometryTile; class RenderedQueryOptions; - -namespace style { -class Style; -} // namespace style +class RenderLayer; class CollisionTile; class CanonicalTileID; @@ -45,9 +41,9 @@ public: const RenderedQueryOptions& options, const GeometryTileData&, const CanonicalTileID&, - const style::Style&, + const std::vector<const RenderLayer*>&, const CollisionTile*, - const GeometryTile& tile) const; + const float additionalQueryRadius) const; static optional<GeometryCoordinates> translateQueryGeometry( const GeometryCoordinates& queryGeometry, @@ -66,7 +62,7 @@ private: const RenderedQueryOptions& options, const GeometryTileData&, const CanonicalTileID&, - const style::Style&, + const std::vector<const RenderLayer*>&, const float bearing, const float pixelsToTileUnits) const; diff --git a/src/mbgl/gl/attribute.cpp b/src/mbgl/gl/attribute.cpp index 4dd102400d..bb5b2ddc34 100644 --- a/src/mbgl/gl/attribute.cpp +++ b/src/mbgl/gl/attribute.cpp @@ -4,9 +4,35 @@ namespace mbgl { namespace gl { -AttributeLocation bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) { +void bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) { + if (location >= MAX_ATTRIBUTES) { + throw gl::Error("too many vertex attributes"); + } MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name)); - return location; +} + +std::set<std::string> getActiveAttributes(ProgramID id) { + std::set<std::string> activeAttributes; + + GLint attributeCount; + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, &attributeCount)); + + GLint maxAttributeLength; + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeLength)); + + std::string attributeName; + attributeName.resize(maxAttributeLength); + + GLsizei actualLength; + GLint size; + GLenum type; + + for (int32_t i = 0; i < attributeCount; i++) { + MBGL_CHECK_ERROR(glGetActiveAttrib(id, i, maxAttributeLength, &actualLength, &size, &type, &attributeName[0])); + activeAttributes.emplace(std::string(attributeName, 0, actualLength)); + } + + return activeAttributes; } } // namespace gl diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp index 2d1d2386eb..fa6c2ddeab 100644 --- a/src/mbgl/gl/attribute.hpp +++ b/src/mbgl/gl/attribute.hpp @@ -8,6 +8,7 @@ #include <cstddef> #include <vector> +#include <set> #include <functional> #include <string> #include <array> @@ -213,7 +214,8 @@ const std::size_t Vertex<A1, A2, A3, A4, A5>::attributeOffsets[5] = { } // namespace detail -AttributeLocation bindAttributeLocation(ProgramID, AttributeLocation, const char * name); +void bindAttributeLocation(ProgramID, AttributeLocation, const char * name); +std::set<std::string> getActiveAttributes(ProgramID); template <class... As> class Attributes { @@ -229,11 +231,20 @@ public: using Vertex = detail::Vertex<typename As::Type...>; - template <class A> - static constexpr std::size_t Index = TypeIndex<A, As...>::value; - static Locations bindLocations(const ProgramID& id) { - return Locations { bindAttributeLocation(id, Index<As>, As::name())... }; + std::set<std::string> activeAttributes = getActiveAttributes(id); + + AttributeLocation location = 0; + auto maybeBindLocation = [&](const char* name) -> optional<AttributeLocation> { + if (activeAttributes.count(name)) { + bindAttributeLocation(id, location, name); + return location++; + } else { + return {}; + } + }; + + return Locations { maybeBindLocation(As::name())... }; } template <class Program> @@ -257,7 +268,7 @@ public: template <class DrawMode> static Bindings bindings(const VertexBuffer<Vertex, DrawMode>& buffer) { - return Bindings { As::Type::binding(buffer, Index<As>)... }; + return Bindings { As::Type::binding(buffer, TypeIndex<As, As...>::value)... }; } static Bindings offsetBindings(const Bindings& bindings, std::size_t vertexOffset) { diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index a530528da1..d0c538efb0 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -1,4 +1,3 @@ -#include <mbgl/map/view.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/gl/gl.hpp> #include <mbgl/gl/debugging_extension.hpp> @@ -16,6 +15,31 @@ namespace gl { static_assert(underlying_type(ShaderType::Vertex) == GL_VERTEX_SHADER, "OpenGL type mismatch"); static_assert(underlying_type(ShaderType::Fragment) == GL_FRAGMENT_SHADER, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::Byte) == GL_BYTE, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::UnsignedByte) == GL_UNSIGNED_BYTE, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::Short) == GL_SHORT, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::UnsignedShort) == GL_UNSIGNED_SHORT, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::Integer) == GL_INT, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::UnsignedInteger) == GL_UNSIGNED_INT, "OpenGL type mismatch"); +static_assert(underlying_type(DataType::Float) == GL_FLOAT, "OpenGL type mismatch"); + +#if not MBGL_USE_GLES2 +static_assert(underlying_type(RenderbufferType::RGBA) == GL_RGBA8, "OpenGL type mismatch"); +#else +static_assert(underlying_type(RenderbufferType::RGBA) == GL_RGBA8_OES, "OpenGL type mismatch"); +#endif // MBGL_USE_GLES2 +#if not MBGL_USE_GLES2 +static_assert(underlying_type(RenderbufferType::DepthStencil) == GL_DEPTH24_STENCIL8, "OpenGL type mismatch"); +#else +static_assert(underlying_type(RenderbufferType::DepthStencil) == GL_DEPTH24_STENCIL8_OES, "OpenGL type mismatch"); +#endif // MBGL_USE_GLES2 +#if not MBGL_USE_GLES2 +static_assert(underlying_type(RenderbufferType::DepthComponent) == GL_DEPTH_COMPONENT, "OpenGL type mismatch"); +#else +static_assert(underlying_type(RenderbufferType::DepthComponent) == GL_DEPTH_COMPONENT16, "OpenGL type mismatch"); +#endif // MBGL_USE_GLES2 + + static_assert(underlying_type(PrimitiveType::Points) == GL_POINTS, "OpenGL type mismatch"); static_assert(underlying_type(PrimitiveType::Lines) == GL_LINES, "OpenGL type mismatch"); static_assert(underlying_type(PrimitiveType::LineLoop) == GL_LINE_LOOP, "OpenGL type mismatch"); @@ -36,16 +60,40 @@ static_assert(std::is_same<std::underlying_type_t<TextureFormat>, GLenum>::value static_assert(underlying_type(TextureFormat::RGBA) == GL_RGBA, "OpenGL type mismatch"); static_assert(underlying_type(TextureFormat::Alpha) == GL_ALPHA, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::Float) == GL_FLOAT, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatVec2) == GL_FLOAT_VEC2, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatVec3) == GL_FLOAT_VEC3, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatVec4) == GL_FLOAT_VEC4, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::Int) == GL_INT, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::IntVec2) == GL_INT_VEC2, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::IntVec3) == GL_INT_VEC3, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::IntVec4) == GL_INT_VEC4, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::Bool) == GL_BOOL, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::BoolVec2) == GL_BOOL_VEC2, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::BoolVec3) == GL_BOOL_VEC3, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::BoolVec4) == GL_BOOL_VEC4, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatMat2) == GL_FLOAT_MAT2, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatMat3) == GL_FLOAT_MAT3, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::FloatMat4) == GL_FLOAT_MAT4, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::Sampler2D) == GL_SAMPLER_2D, "OpenGL type mismatch"); +static_assert(underlying_type(UniformDataType::SamplerCube) == GL_SAMPLER_CUBE, "OpenGL type mismatch"); + +static_assert(underlying_type(BufferUsage::StreamDraw) == GL_STREAM_DRAW, "OpenGL type mismatch"); +static_assert(underlying_type(BufferUsage::StaticDraw) == GL_STATIC_DRAW, "OpenGL type mismatch"); +static_assert(underlying_type(BufferUsage::DynamicDraw) == GL_DYNAMIC_DRAW, "OpenGL type mismatch"); + static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mismatch"); Context::Context() = default; Context::~Context() { - reset(); + if (cleanupOnDestruction) { + reset(); + } } void Context::initializeExtensions(const std::function<gl::ProcAddress(const char*)>& getProcAddress) { - if (const char* extensions = + if (const auto* extensions = reinterpret_cast<const char*>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)))) { auto fn = [&]( @@ -94,7 +142,7 @@ UniqueShader Context::createShader(ShaderType type, const std::string& source) { UniqueShader result { MBGL_CHECK_ERROR(glCreateShader(static_cast<GLenum>(type))), { this } }; const GLchar* sources = source.data(); - const GLsizei lengths = static_cast<GLsizei>(source.length()); + const auto lengths = static_cast<GLsizei>(source.length()); MBGL_CHECK_ERROR(glShaderSource(result, 1, &sources, &lengths)); MBGL_CHECK_ERROR(glCompileShader(result)); @@ -164,15 +212,20 @@ void Context::verifyProgramLinkage(ProgramID program_) { throw std::runtime_error("program failed to link"); } -UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size) { +UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size, const BufferUsage usage) { BufferID id = 0; MBGL_CHECK_ERROR(glGenBuffers(1, &id)); UniqueBuffer result { std::move(id), { this } }; vertexBuffer = result; - MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); + MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, static_cast<GLenum>(usage))); return result; } +void Context::updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size) { + vertexBuffer = buffer; + MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data)); +} + UniqueBuffer Context::createIndexBuffer(const void* data, std::size_t size) { BufferID id = 0; MBGL_CHECK_ERROR(glGenBuffers(1, &id)); @@ -278,11 +331,9 @@ std::unique_ptr<uint8_t[]> Context::readFramebuffer(const Size size, const Textu const size_t stride = size.width * (format == TextureFormat::RGBA ? 4 : 1); auto data = std::make_unique<uint8_t[]>(stride * size.height); -#if not MBGL_USE_GLES2 // When reading data from the framebuffer, make sure that we are storing the values // tightly packed into the buffer to avoid buffer overruns. pixelStorePack = { 1 }; -#endif // MBGL_USE_GLES2 MBGL_CHECK_ERROR(glReadPixels(0, 0, size.width, size.height, static_cast<GLenum>(format), GL_UNSIGNED_BYTE, data.get())); @@ -408,6 +459,9 @@ Framebuffer Context::createFramebuffer(const Texture& color) { Framebuffer Context::createFramebuffer(const Texture& color, const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) { + if (color.size != depthTarget.size) { + throw std::runtime_error("Renderbuffer size mismatch"); + } auto fbo = createFramebuffer(); bindFramebuffer = fbo; MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0)); @@ -419,6 +473,7 @@ Context::createFramebuffer(const Texture& color, UniqueTexture Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit) { auto obj = createTexture(); + pixelStoreUnpack = { 1 }; updateTexture(obj, size, data, format, unit); // We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures. // We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus. @@ -431,7 +486,7 @@ Context::createTexture(const Size size, const void* data, TextureFormat format, void Context::updateTexture( TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit) { - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = id; MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLenum>(format), size.width, size.height, 0, static_cast<GLenum>(format), GL_UNSIGNED_BYTE, @@ -445,7 +500,7 @@ void Context::bindTexture(Texture& obj, TextureWrap wrapX, TextureWrap wrapY) { if (filter != obj.filter || mipmap != obj.mipmap || wrapX != obj.wrapX || wrapY != obj.wrapY) { - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = obj.texture; if (filter != obj.filter || mipmap != obj.mipmap) { @@ -476,7 +531,7 @@ void Context::bindTexture(Texture& obj, } else if (texture[unit] != obj.texture) { // We are checking first to avoid setting the active texture without a subsequent // texture bind. - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = obj.texture; } } @@ -488,8 +543,8 @@ void Context::reset() { } void Context::setDirtyState() { - // Note: does not set viewport/bindFramebuffer to dirty since they are handled separately in - // the view object. + // Note: does not set viewport/scissorTest/bindFramebuffer to dirty + // since they are handled separately in the view object. stencilFunc.setDirty(); stencilMask.setDirty(); stencilTest.setDirty(); @@ -508,13 +563,13 @@ void Context::setDirtyState() { clearStencil.setDirty(); program.setDirty(); lineWidth.setDirty(); - activeTexture.setDirty(); + activeTextureUnit.setDirty(); + pixelStorePack.setDirty(); + pixelStoreUnpack.setDirty(); #if not MBGL_USE_GLES2 pointSize.setDirty(); pixelZoom.setDirty(); rasterPos.setDirty(); - pixelStorePack.setDirty(); - pixelStoreUnpack.setDirty(); pixelTransferDepth.setDirty(); pixelTransferStencil.setDirty(); #endif // MBGL_USE_GLES2 @@ -659,8 +714,10 @@ void Context::performCleanup() { if (!abandonedTextures.empty()) { for (const auto id : abandonedTextures) { - if (activeTexture == id) { - activeTexture.setDirty(); + for (auto& binding : texture) { + if (binding == id) { + binding.setDirty(); + } } } MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data())); diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 8929d24e54..528113cbba 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -25,9 +25,6 @@ #include <string> namespace mbgl { - -class View; - namespace gl { constexpr size_t TextureMax = 64; @@ -64,13 +61,19 @@ public: optional<std::pair<BinaryProgramFormat, std::string>> getBinaryProgram(ProgramID) const; template <class Vertex, class DrawMode> - VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v) { + VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v, const BufferUsage usage=BufferUsage::StaticDraw) { return VertexBuffer<Vertex, DrawMode> { v.vertexSize(), - createVertexBuffer(v.data(), v.byteSize()) + createVertexBuffer(v.data(), v.byteSize(), usage) }; } + template <class Vertex, class DrawMode> + void updateVertexBuffer(VertexBuffer<Vertex, DrawMode>& buffer, VertexVector<Vertex, DrawMode>&& v) { + assert(v.vertexSize() == buffer.vertexCount); + updateVertexBuffer(buffer.buffer, v.data(), v.byteSize()); + } + template <class DrawMode> IndexBuffer<DrawMode> createIndexBuffer(IndexVector<DrawMode>&& v) { return IndexBuffer<DrawMode> { @@ -187,7 +190,13 @@ public: return vertexArray.get(); } + void setCleanupOnDestruction(bool cleanup) { + cleanupOnDestruction = cleanup; + } + private: + bool cleanupOnDestruction = true; + std::unique_ptr<extension::Debugging> debugging; std::unique_ptr<extension::VertexArray> vertexArray; #if MBGL_HAS_BINARY_PROGRAMS @@ -195,9 +204,10 @@ private: #endif public: - State<value::ActiveTexture> activeTexture; + State<value::ActiveTextureUnit> activeTextureUnit; State<value::BindFramebuffer> bindFramebuffer; State<value::Viewport> viewport; + State<value::ScissorTest> scissorTest; std::array<State<value::BindTexture>, 2> texture; State<value::Program> program; State<value::BindVertexBuffer> vertexBuffer; @@ -205,11 +215,12 @@ public: State<value::BindVertexArray, const Context&> bindVertexArray { *this }; VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }), *this }; + State<value::PixelStorePack> pixelStorePack; + State<value::PixelStoreUnpack> pixelStoreUnpack; + #if not MBGL_USE_GLES2 State<value::PixelZoom> pixelZoom; State<value::RasterPos> rasterPos; - State<value::PixelStorePack> pixelStorePack; - State<value::PixelStoreUnpack> pixelStoreUnpack; State<value::PixelTransferDepth> pixelTransferDepth; State<value::PixelTransferStencil> pixelTransferStencil; #endif // MBGL_USE_GLES2 @@ -237,7 +248,8 @@ private: State<value::PointSize> pointSize; #endif // MBGL_USE_GLES2 - UniqueBuffer createVertexBuffer(const void* data, std::size_t size); + UniqueBuffer createVertexBuffer(const void* data, std::size_t size, const BufferUsage usage); + void updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size); UniqueBuffer createIndexBuffer(const void* data, std::size_t size); UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit); void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit); diff --git a/src/mbgl/gl/debugging.cpp b/src/mbgl/gl/debugging.cpp index 0d69d58be5..366b4d63c7 100644 --- a/src/mbgl/gl/debugging.cpp +++ b/src/mbgl/gl/debugging.cpp @@ -10,9 +10,9 @@ namespace gl { DebugGroup::DebugGroup(const Context& context_, const std::string& name) : context(context_) { if (auto debugging = context.getDebuggingExtension()) { if (debugging->pushDebugGroup) { - debugging->pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(name.size()), name.c_str()); + MBGL_CHECK_ERROR(debugging->pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(name.size()), name.c_str())); } else if (debugging->pushGroupMarkerEXT) { - debugging->pushGroupMarkerEXT(GLsizei(name.size() + 1), name.c_str()); + MBGL_CHECK_ERROR(debugging->pushGroupMarkerEXT(GLsizei(name.size() + 1), name.c_str())); } } } @@ -20,9 +20,9 @@ DebugGroup::DebugGroup(const Context& context_, const std::string& name) : conte DebugGroup::~DebugGroup() { if (auto debugging = context.getDebuggingExtension()) { if (debugging->popDebugGroup) { - debugging->popDebugGroup(); + MBGL_CHECK_ERROR(debugging->popDebugGroup()); } else if (debugging->popGroupMarkerEXT) { - debugging->popGroupMarkerEXT(); + MBGL_CHECK_ERROR(debugging->popGroupMarkerEXT()); } } } diff --git a/src/mbgl/gl/debugging_extension.hpp b/src/mbgl/gl/debugging_extension.hpp index c1835cfcdd..5657bbde88 100644 --- a/src/mbgl/gl/debugging_extension.hpp +++ b/src/mbgl/gl/debugging_extension.hpp @@ -53,13 +53,13 @@ namespace extension { class Debugging { public: - typedef void (*Callback)(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam); + using Callback = void (*)(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam); static void DebugCallback(GLenum source, GLenum type, diff --git a/src/mbgl/gl/gl.cpp b/src/mbgl/gl/gl.cpp index 8999468dbd..bd6d7b192d 100644 --- a/src/mbgl/gl/gl.cpp +++ b/src/mbgl/gl/gl.cpp @@ -1,12 +1,13 @@ #include <mbgl/gl/gl.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/util.hpp> namespace mbgl { namespace gl { namespace { -constexpr const char* stringFromError(GLenum err) { +MBGL_CONSTEXPR const char* stringFromError(GLenum err) { switch (err) { case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; diff --git a/src/mbgl/gl/index_buffer.hpp b/src/mbgl/gl/index_buffer.hpp index b3610f4154..01b6396e00 100644 --- a/src/mbgl/gl/index_buffer.hpp +++ b/src/mbgl/gl/index_buffer.hpp @@ -24,7 +24,9 @@ public: std::size_t byteSize() const { return v.size() * sizeof(uint16_t); } bool empty() const { return v.empty(); } + void clear() { v.clear(); } const uint16_t* data() const { return v.data(); } + const std::vector<uint16_t>& vector() const { return v; } private: std::vector<uint16_t> v; diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index 1a429c6630..3b54ec194a 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -34,15 +34,17 @@ public: : program( context.createProgram(context.createShader(ShaderType::Vertex, vertexSource), context.createShader(ShaderType::Fragment, fragmentSource))), - attributeLocations(Attributes::bindLocations(program)), - uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))) { + uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))), + attributeLocations(Attributes::bindLocations(program)) { + // Re-link program after manually binding only active attributes in Attributes::bindLocations + context.linkProgram(program); } template <class BinaryProgram> Program(Context& context, const BinaryProgram& binaryProgram) : program(context.createProgram(binaryProgram.format(), binaryProgram.code())), - attributeLocations(Attributes::loadNamedLocations(binaryProgram)), - uniformsState(Uniforms::loadNamedLocations(binaryProgram)) { + uniformsState(Uniforms::loadNamedLocations(binaryProgram)), + attributeLocations(Attributes::loadNamedLocations(binaryProgram)) { } static Program createProgram(gl::Context& context, @@ -140,8 +142,8 @@ public: private: UniqueProgram program; - typename Attributes::Locations attributeLocations; typename Uniforms::State uniformsState; + typename Attributes::Locations attributeLocations; }; } // namespace gl diff --git a/src/mbgl/gl/renderbuffer.hpp b/src/mbgl/gl/renderbuffer.hpp index cc8ff13268..0592557a7f 100644 --- a/src/mbgl/gl/renderbuffer.hpp +++ b/src/mbgl/gl/renderbuffer.hpp @@ -9,9 +9,23 @@ namespace gl { template <RenderbufferType renderbufferType> class Renderbuffer { public: + Renderbuffer(Size size_, UniqueRenderbuffer renderbuffer_, bool dirty_ = false) + : size(std::move(size_)), renderbuffer(std::move(renderbuffer_)), dirty(dirty_) { + } + using type = std::integral_constant<RenderbufferType, renderbufferType>; Size size; - gl::UniqueRenderbuffer renderbuffer; + UniqueRenderbuffer renderbuffer; + + void shouldClear(bool clear) { + dirty = clear; + } + bool needsClearing() { + return dirty; + } + +private: + bool dirty; }; } // namespace gl diff --git a/src/mbgl/gl/texture.hpp b/src/mbgl/gl/texture.hpp index 5330689ac2..625e69233a 100644 --- a/src/mbgl/gl/texture.hpp +++ b/src/mbgl/gl/texture.hpp @@ -8,12 +8,24 @@ namespace gl { class Texture { public: + Texture(Size size_, UniqueTexture texture_, + TextureFilter filter_ = TextureFilter::Nearest, + TextureMipMap mipmap_ = TextureMipMap::No, + TextureWrap wrapX_ = TextureWrap::Clamp, + TextureWrap wrapY_ = TextureWrap::Clamp) + : size(std::move(size_)), + texture(std::move(texture_)), + filter(filter_), + mipmap(mipmap_), + wrapX(wrapX_), + wrapY(wrapY_) {} + Size size; UniqueTexture texture; - TextureFilter filter = TextureFilter::Nearest; - TextureMipMap mipmap = TextureMipMap::No; - TextureWrap wrapX = TextureWrap::Clamp; - TextureWrap wrapY = TextureWrap::Clamp; + TextureFilter filter; + TextureMipMap mipmap; + TextureWrap wrapX; + TextureWrap wrapY; }; } // namespace gl diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index e7d2ca449a..da08195e58 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -15,8 +15,16 @@ using VertexArrayID = uint32_t; using FramebufferID = uint32_t; using RenderbufferID = uint32_t; +// OpenGL does not formally define a type for attribute locations, but most APIs use +// GLuint. The exception is glGetAttribLocation, which returns GLint so that -1 can +// be used as an error indicator. using AttributeLocation = uint32_t; + +// OpenGL does not formally define a type for uniform locations, but all APIs use GLint. +// The value -1 is special, typically used as a placeholder for an unused uniform and +// "silently ignored". using UniformLocation = int32_t; + using TextureUnit = uint8_t; enum class ShaderType : uint32_t { @@ -66,8 +74,6 @@ enum class PrimitiveType { TriangleFan = 0x0006 }; -#if not MBGL_USE_GLES2 - struct PixelStorageType { int32_t alignment; }; @@ -76,9 +82,33 @@ constexpr bool operator!=(const PixelStorageType& a, const PixelStorageType& b) return a.alignment != b.alignment; } -#endif // MBGL_USE_GLES2 - using BinaryProgramFormat = uint32_t; +enum class UniformDataType : uint32_t { + Float = 0x1406, + FloatVec2 = 0x8B50, + FloatVec3 = 0x8B51, + FloatVec4 = 0x8B52, + Int = 0x1404, + IntVec2 = 0x8B53, + IntVec3 = 0x8B54, + IntVec4 = 0x8B55, + Bool = 0x8B56, + BoolVec2 = 0x8B57, + BoolVec3 = 0x8B58, + BoolVec4 = 0x8B59, + FloatMat2 = 0x8B5A, + FloatMat3 = 0x8B5B, + FloatMat4 = 0x8B5C, + Sampler2D = 0x8B5E, + SamplerCube = 0x8B60, +}; + +enum class BufferUsage : uint32_t { + StreamDraw = 0x88E0, + StaticDraw = 0x88E4, + DynamicDraw = 0x88E8, +}; + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/uniform.cpp b/src/mbgl/gl/uniform.cpp index 7b674f2cde..2946980c19 100644 --- a/src/mbgl/gl/uniform.cpp +++ b/src/mbgl/gl/uniform.cpp @@ -4,6 +4,8 @@ #include <mbgl/util/size.hpp> #include <mbgl/util/convert.hpp> +#include <memory> + namespace mbgl { namespace gl { @@ -79,5 +81,96 @@ void bindUniform<std::array<uint16_t, 2>>(UniformLocation location, const std::a // Add more as needed. +#ifndef NDEBUG + +ActiveUniforms activeUniforms(ProgramID id) { + ActiveUniforms active; + + GLint count; + GLint maxLength; + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &count)); + MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength)); + + auto name = std::make_unique<GLchar[]>(maxLength); + GLsizei length; + GLint size; + GLenum type; + for (GLint index = 0; index < count; index++) { + MBGL_CHECK_ERROR( + glGetActiveUniform(id, index, maxLength, &length, &size, &type, name.get())); + active.emplace( + std::string{ name.get(), static_cast<size_t>(length) }, + ActiveUniform{ static_cast<size_t>(size), static_cast<UniformDataType>(type) }); + } + + return active; +} + +template <> +bool verifyUniform<float>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::Float); + return true; +} + +template <> +bool verifyUniform<std::array<float, 2>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec2); + return true; +} + +template <> +bool verifyUniform<std::array<float, 3>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec3); + return true; +} + +template <> +bool verifyUniform<std::array<double, 16>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatMat4); + return true; +} + +template <> +bool verifyUniform<bool>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::Bool || + uniform.type == UniformDataType::Int || + uniform.type == UniformDataType::Float)); + return true; +} + +template <> +bool verifyUniform<uint8_t>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::Int || + uniform.type == UniformDataType::Float || + uniform.type == UniformDataType::Sampler2D)); + return true; +} + +template <> +bool verifyUniform<Color>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec4); + return true; +} + +template <> +bool verifyUniform<Size>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec2); + return true; +} + +template <> +bool verifyUniform<std::array<uint16_t, 2>>(const ActiveUniform& uniform) { + assert(uniform.size == 1 && + (uniform.type == UniformDataType::IntVec2 || + uniform.type == UniformDataType::FloatVec2)); + return true; +} + +// Add more as needed. + +#endif + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/uniform.hpp b/src/mbgl/gl/uniform.hpp index 3cac09c526..5a78068fc8 100644 --- a/src/mbgl/gl/uniform.hpp +++ b/src/mbgl/gl/uniform.hpp @@ -7,6 +7,7 @@ #include <array> #include <vector> +#include <map> #include <functional> namespace mbgl { @@ -22,13 +23,33 @@ public: T t; }; +class ActiveUniform { +public: + std::size_t size; + UniformDataType type; +}; + +#ifndef NDEBUG + +template <class T> +bool verifyUniform(const ActiveUniform&); + +using ActiveUniforms = std::map<std::string, ActiveUniform>; +ActiveUniforms activeUniforms(ProgramID); + +#endif + template <class Tag, class T> class Uniform { public: using Value = UniformValue<Tag, T>; + using Type = T; + class State { public: + State(UniformLocation location_) : location(std::move(location_)) {} + void operator=(const Value& value) { if (location >= 0 && (!current || *current != value.t)) { current = value.t; @@ -70,12 +91,24 @@ public: using NamedLocations = std::vector<std::pair<const std::string, UniformLocation>>; static State bindLocations(const ProgramID& id) { +#ifndef NDEBUG + // Verify active uniform types match the enum + const auto active = activeUniforms(id); + + util::ignore( + { // Some shader programs have uniforms declared, but not used, so they're not active. + // Therefore, we'll only verify them when they are indeed active. + (active.find(Us::name()) != active.end() + ? verifyUniform<typename Us::Type>(active.at(Us::name())) + : false)... }); +#endif + return State { { uniformLocation(id, Us::name()) }... }; } template <class Program> static State loadNamedLocations(const Program& program) { - return State{ { program.uniformLocation(Us::name()) }... }; + return State(typename Us::State(program.uniformLocation(Us::name()))...); } static NamedLocations getNamedLocations(const State& state) { diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp index 10818e8499..092403af0d 100644 --- a/src/mbgl/gl/value.cpp +++ b/src/mbgl/gl/value.cpp @@ -242,13 +242,13 @@ LineWidth::Type LineWidth::Get() { return lineWidth; } -const constexpr ActiveTexture::Type ActiveTexture::Default; +const constexpr ActiveTextureUnit::Type ActiveTextureUnit::Default; -void ActiveTexture::Set(const Type& value) { +void ActiveTextureUnit::Set(const Type& value) { MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value)); } -ActiveTexture::Type ActiveTexture::Get() { +ActiveTextureUnit::Type ActiveTextureUnit::Get() { GLint activeTexture; MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture)); return static_cast<Type>(activeTexture - GL_TEXTURE0); @@ -267,6 +267,18 @@ Viewport::Type Viewport::Get() { { static_cast<uint32_t>(viewport[2]), static_cast<uint32_t>(viewport[3]) } }; } +const constexpr ScissorTest::Type ScissorTest::Default; + +void ScissorTest::Set(const Type& value) { + MBGL_CHECK_ERROR(value ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST)); +} + +ScissorTest::Type ScissorTest::Get() { + Type scissorTest; + MBGL_CHECK_ERROR(scissorTest = glIsEnabled(GL_SCISSOR_TEST)); + return scissorTest; +} + const constexpr BindFramebuffer::Type BindFramebuffer::Default; void BindFramebuffer::Set(const Type& value) { @@ -371,6 +383,34 @@ void VertexAttribute::Set(const optional<AttributeBinding>& binding, Context& co } } +const constexpr PixelStorePack::Type PixelStorePack::Default; + +void PixelStorePack::Set(const Type& value) { + assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || + value.alignment == 8); + MBGL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, value.alignment)); +} + +PixelStorePack::Type PixelStorePack::Get() { + Type value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_PACK_ALIGNMENT, &value.alignment)); + return value; +} + +const constexpr PixelStoreUnpack::Type PixelStoreUnpack::Default; + +void PixelStoreUnpack::Set(const Type& value) { + assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || + value.alignment == 8); + MBGL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, value.alignment)); +} + +PixelStoreUnpack::Type PixelStoreUnpack::Get() { + Type value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_UNPACK_ALIGNMENT, &value.alignment)); + return value; +} + #if not MBGL_USE_GLES2 const constexpr PointSize::Type PointSize::Default; @@ -410,34 +450,6 @@ RasterPos::Type RasterPos::Get() { return { pos[0], pos[1], pos[2], pos[3] }; } -const constexpr PixelStorePack::Type PixelStorePack::Default; - -void PixelStorePack::Set(const Type& value) { - assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || - value.alignment == 8); - MBGL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, value.alignment)); -} - -PixelStorePack::Type PixelStorePack::Get() { - Type value; - MBGL_CHECK_ERROR(glGetIntegerv(GL_PACK_ALIGNMENT, &value.alignment)); - return value; -} - -const constexpr PixelStoreUnpack::Type PixelStoreUnpack::Default; - -void PixelStoreUnpack::Set(const Type& value) { - assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 || - value.alignment == 8); - MBGL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, value.alignment)); -} - -PixelStoreUnpack::Type PixelStoreUnpack::Get() { - Type value; - MBGL_CHECK_ERROR(glGetIntegerv(GL_UNPACK_ALIGNMENT, &value.alignment)); - return value; -} - const constexpr PixelTransferDepth::Type PixelTransferDepth::Default; void PixelTransferDepth::Set(const Type& value) { diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp index 62fe88a2f4..7b85a5ff4b 100644 --- a/src/mbgl/gl/value.hpp +++ b/src/mbgl/gl/value.hpp @@ -165,7 +165,7 @@ struct LineWidth { static Type Get(); }; -struct ActiveTexture { +struct ActiveTextureUnit { using Type = TextureUnit; static const constexpr Type Default = 0; static void Set(const Type&); @@ -183,6 +183,13 @@ struct Viewport { static Type Get(); }; +struct ScissorTest { + using Type = bool; + static const constexpr Type Default = false; + static void Set(const Type&); + static Type Get(); +}; + constexpr bool operator!=(const Viewport::Type& a, const Viewport::Type& b) { return a.x != b.x || a.y != b.y || a.size != b.size; } @@ -239,6 +246,20 @@ struct VertexAttribute { static void Set(const Type&, Context&, AttributeLocation); }; +struct PixelStorePack { + using Type = PixelStorageType; + static const constexpr Type Default = { 4 }; + static void Set(const Type&); + static Type Get(); +}; + +struct PixelStoreUnpack { + using Type = PixelStorageType; + static const constexpr Type Default = { 4 }; + static void Set(const Type&); + static Type Get(); +}; + #if not MBGL_USE_GLES2 struct PointSize { @@ -278,20 +299,6 @@ constexpr bool operator!=(const RasterPos::Type& a, const RasterPos::Type& b) { return a.x != b.x || a.y != b.y || a.z != b.z || a.w != b.w; } -struct PixelStorePack { - using Type = PixelStorageType; - static const constexpr Type Default = { 4 }; - static void Set(const Type&); - static Type Get(); -}; - -struct PixelStoreUnpack { - using Type = PixelStorageType; - static const constexpr Type Default = { 4 }; - static void Set(const Type&); - static Type Get(); -}; - struct PixelTransferDepth { struct Type { float scale; diff --git a/src/mbgl/gl/vertex_buffer.hpp b/src/mbgl/gl/vertex_buffer.hpp index c9bc01f3e8..9f8b156b25 100644 --- a/src/mbgl/gl/vertex_buffer.hpp +++ b/src/mbgl/gl/vertex_buffer.hpp @@ -26,7 +26,9 @@ public: std::size_t byteSize() const { return v.size() * sizeof(Vertex); } bool empty() const { return v.empty(); } + void clear() { v.clear(); } const Vertex* data() const { return v.data(); } + const std::vector<Vertex>& vector() const { return v; } private: std::vector<Vertex> v; diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 8816f4c95c..02fb800df6 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -5,8 +5,8 @@ namespace mbgl { using namespace style; -SymbolInstance::SymbolInstance(Anchor& anchor, - const GeometryCoordinates& line, +SymbolInstance::SymbolInstance(Anchor& anchor_, + GeometryCoordinates line_, const std::pair<Shaping, Shaping>& shapedTextOrientations, optional<PositionedIcon> shapedIcon, const SymbolLayoutProperties::Evaluated& layout, @@ -16,33 +16,38 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const float textBoxScale, const float textPadding, const SymbolPlacementType textPlacement, + const std::array<float, 2> textOffset_, const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement, - const GlyphPositions& face, + const std::array<float, 2> iconOffset_, + const GlyphPositionMap& positions, const IndexedSubfeature& indexedFeature, const std::size_t featureIndex_) : - point(anchor.point), + anchor(anchor_), + line(line_), index(index_), hasText(shapedTextOrientations.first || shapedTextOrientations.second), hasIcon(shapedIcon), // Create the collision features that will be used to check whether this symbol instance can be placed - textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), - iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), - featureIndex(featureIndex_) { + textCollisionFeature(line_, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), + iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), + featureIndex(featureIndex_), + textOffset(textOffset_), + iconOffset(iconOffset_) { // Create the quads used for rendering the icon and glyphs. if (addToBuffers) { if (shapedIcon) { - iconQuad = getIconQuad(anchor, *shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first); + iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first); } if (shapedTextOrientations.first) { - auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, face); + auto quads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions); glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); } if (shapedTextOrientations.second) { - auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, face); + auto quads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions); glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); } } diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index eadbf67475..f1df416cd1 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -1,7 +1,7 @@ #pragma once #include <mbgl/text/quads.hpp> -#include <mbgl/text/glyph.hpp> +#include <mbgl/text/glyph_atlas.hpp> #include <mbgl/text/collision_feature.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> @@ -13,7 +13,7 @@ class IndexedSubfeature; class SymbolInstance { public: SymbolInstance(Anchor& anchor, - const GeometryCoordinates& line, + GeometryCoordinates line, const std::pair<Shaping, Shaping>& shapedTextOrientations, optional<PositionedIcon> shapedIcon, const style::SymbolLayoutProperties::Evaluated&, @@ -23,14 +23,17 @@ public: const float textBoxScale, const float textPadding, style::SymbolPlacementType textPlacement, + const std::array<float, 2> textOffset, const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement, - const GlyphPositions& face, + const std::array<float, 2> iconOffset, + const GlyphPositionMap&, const IndexedSubfeature&, const std::size_t featureIndex); - Point<float> point; + Anchor anchor; + GeometryCoordinates line; uint32_t index; bool hasText; bool hasIcon; @@ -40,6 +43,8 @@ public: CollisionFeature iconCollisionFeature; WritingModeType writingModes; std::size_t featureIndex; + std::array<float, 2> textOffset; + std::array<float, 2> iconOffset; }; } // namespace mbgl diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 2accac281b..2c90b69b08 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -1,12 +1,12 @@ #include <mbgl/layout/symbol_layout.hpp> #include <mbgl/layout/merge_lines.hpp> #include <mbgl/layout/clip_lines.hpp> -#include <mbgl/renderer/symbol_bucket.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> #include <mbgl/style/filter_evaluator.hpp> #include <mbgl/renderer/bucket_parameters.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/image_atlas.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/text/get_anchors.hpp> #include <mbgl/text/collision_tile.hpp> #include <mbgl/text/shaping.hpp> @@ -40,21 +40,22 @@ static bool has(const style::SymbolLayoutProperties::PossiblyEvaluated& layout) SymbolLayout::SymbolLayout(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers, - const GeometryTileLayer& sourceLayer, - IconDependencies& iconDependencies, + std::unique_ptr<GeometryTileLayer> sourceLayer_, + ImageDependencies& imageDependencies, GlyphDependencies& glyphDependencies) - : sourceLayerName(sourceLayer.getName()), + : sourceLayer(std::move(sourceLayer_)), bucketName(layers.at(0)->getID()), overscaling(parameters.tileID.overscaleFactor()), zoom(parameters.tileID.overscaledZ), mode(parameters.mode), + pixelRatio(parameters.pixelRatio), tileSize(util::tileSize * overscaling), tilePixelRatio(float(util::EXTENT) / tileSize), - textSize(layers.at(0)->as<RenderSymbolLayer>()->impl->layout.unevaluated.get<TextSize>()), - iconSize(layers.at(0)->as<RenderSymbolLayer>()->impl->layout.unevaluated.get<IconSize>()) + textSize(layers.at(0)->as<RenderSymbolLayer>()->impl().layout.get<TextSize>()), + iconSize(layers.at(0)->as<RenderSymbolLayer>()->impl().layout.get<IconSize>()) { - const SymbolLayer::Impl& leader = *layers.at(0)->as<RenderSymbolLayer>()->impl; + const SymbolLayer::Impl& leader = layers.at(0)->as<RenderSymbolLayer>()->impl(); layout = leader.layout.evaluate(PropertyEvaluationParameters(zoom)); @@ -74,10 +75,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, } } - // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment` + // If unspecified `*-pitch-alignment` inherits `*-rotation-alignment` if (layout.get<TextPitchAlignment>() == AlignmentType::Auto) { layout.get<TextPitchAlignment>() = layout.get<TextRotationAlignment>(); } + if (layout.get<IconPitchAlignment>() == AlignmentType::Auto) { + layout.get<IconPitchAlignment>() = layout.get<IconRotationAlignment>(); + } const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty(); const bool hasIcon = has<IconImage>(layout); @@ -94,9 +98,9 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, } // Determine glyph dependencies - const size_t featureCount = sourceLayer.featureCount(); + const size_t featureCount = sourceLayer->featureCount(); for (size_t i = 0; i < featureCount; ++i) { - auto feature = sourceLayer.getFeature(i); + auto feature = sourceLayer->getFeature(i); if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) continue; @@ -136,12 +140,17 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, } ft.text = applyArabicShaping(util::utf8_to_utf16::convert(u8string)); + const bool canVerticalizeText = layout.get<TextRotationAlignment>() == AlignmentType::Map + && layout.get<SymbolPlacement>() == SymbolPlacementType::Line + && util::i18n::allowsVerticalWritingMode(*ft.text); // Loop through all characters of this text and collect unique codepoints. for (char16_t chr : *ft.text) { glyphDependencies[layout.get<TextFont>()].insert(chr); - if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) { - glyphDependencies[layout.get<TextFont>()].insert(verticalChr); + if (canVerticalizeText) { + if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) { + glyphDependencies[layout.get<TextFont>()].insert(verticalChr); + } } } } @@ -152,7 +161,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, icon = util::replaceTokens(icon, getValue); } ft.icon = icon; - iconDependencies.insert(*ft.icon); + imageDependencies.insert(*ft.icon); } if (ft.text || ft.icon) { @@ -169,103 +178,67 @@ bool SymbolLayout::hasSymbolInstances() const { return !symbolInstances.empty(); } -void SymbolLayout::prepare(const GlyphPositionMap& glyphs, const IconMap& icons) { - float horizontalAlign = 0.5; - float verticalAlign = 0.5; - - switch (layout.get<TextAnchor>()) { - case TextAnchorType::Top: - case TextAnchorType::Bottom: - case TextAnchorType::Center: - break; - case TextAnchorType::Right: - case TextAnchorType::TopRight: - case TextAnchorType::BottomRight: - horizontalAlign = 1; - break; - case TextAnchorType::Left: - case TextAnchorType::TopLeft: - case TextAnchorType::BottomLeft: - horizontalAlign = 0; - break; - } - - switch (layout.get<TextAnchor>()) { - case TextAnchorType::Left: - case TextAnchorType::Right: - case TextAnchorType::Center: - break; - case TextAnchorType::Bottom: - case TextAnchorType::BottomLeft: - case TextAnchorType::BottomRight: - verticalAlign = 1; - break; - case TextAnchorType::Top: - case TextAnchorType::TopLeft: - case TextAnchorType::TopRight: - verticalAlign = 0; - break; - } - - const float justify = layout.get<TextJustify>() == TextJustifyType::Right ? 1 : - layout.get<TextJustify>() == TextJustifyType::Left ? 0 : - 0.5; - - +void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions, + const ImageMap& imageMap, const ImagePositions& imagePositions) { const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && layout.get<SymbolPlacement>() == SymbolPlacementType::Line; + auto glyphMapIt = glyphMap.find(layout.get<TextFont>()); + const Glyphs& glyphs = glyphMapIt != glyphMap.end() + ? glyphMapIt->second : Glyphs(); + + auto glyphPositionsIt = glyphPositions.find(layout.get<TextFont>()); + const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end() + ? glyphPositionsIt->second : GlyphPositionMap(); + for (auto it = features.begin(); it != features.end(); ++it) { auto& feature = *it; if (feature.geometry.empty()) continue; std::pair<Shaping, Shaping> shapedTextOrientations; optional<PositionedIcon> shapedIcon; - GlyphPositions face; // if feature has text, shape the text if (feature.text) { - auto glyphPositions = glyphs.find(layout.get<TextFont>()); - if (glyphPositions != glyphs.end()) { // If there are no glyphs available for this feature, skip shaping - auto applyShaping = [&] (const std::u16string& text, WritingModeType writingMode) { - const float oneEm = 24.0f; - const Shaping result = getShaping( - /* string */ text, - /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ? - layout.get<TextMaxWidth>() * oneEm : 0, - /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm, - /* horizontalAlign */ horizontalAlign, - /* verticalAlign */ verticalAlign, - /* justify */ justify, - /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get<TextLetterSpacing>() * oneEm : 0.0f, - /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm), - /* verticalHeight */ oneEm, - /* writingMode */ writingMode, - /* bidirectional algorithm object */ bidi, - /* glyphs */ glyphPositions->second); - - return result; - }; - - shapedTextOrientations.first = applyShaping(*feature.text, WritingModeType::Horizontal); - - if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) { - shapedTextOrientations.second = applyShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical); - } + auto applyShaping = [&] (const std::u16string& text, WritingModeType writingMode) { + const float oneEm = 24.0f; + const Shaping result = getShaping( + /* string */ text, + /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ? + layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0, + /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm, + /* anchor */ layout.evaluate<TextAnchor>(zoom, feature), + /* justify */ layout.evaluate<TextJustify>(zoom, feature), + /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * oneEm : 0.0f, + /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm), + /* verticalHeight */ oneEm, + /* writingMode */ writingMode, + /* bidirectional algorithm object */ bidi, + /* glyphs */ glyphs); + + return result; + }; + + shapedTextOrientations.first = applyShaping(*feature.text, WritingModeType::Horizontal); + + if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) { + shapedTextOrientations.second = applyShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical); } } // if feature has icon, get sprite atlas position if (feature.icon) { - auto image = icons.find(*feature.icon); - if (image != icons.end()) { - shapedIcon = PositionedIcon::shapeIcon(image->second, + auto image = imageMap.find(*feature.icon); + if (image != imageMap.end()) { + shapedIcon = PositionedIcon::shapeIcon( + imagePositions.at(*feature.icon), layout.evaluate<IconOffset>(zoom, feature), + layout.evaluate<IconAnchor>(zoom, feature), layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD); - if (image->second.sdf) { + if (image->second->sdf) { sdfIcons = true; } - if (image->second.relativePixelRatio != 1.0f) { + if (image->second->pixelRatio != pixelRatio) { iconsNeedLinear = true; } else if (layout.get<IconRotate>().constantOr(1) != 0) { iconsNeedLinear = true; @@ -275,8 +248,7 @@ void SymbolLayout::prepare(const GlyphPositionMap& glyphs, const IconMap& icons) // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { - auto glyphPositionsIt = glyphs.find(layout.get<TextFont>()); - addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionsIt == glyphs.end() ? GlyphPositions() : glyphPositionsIt->second); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap); } feature.geometry.clear(); @@ -289,12 +261,14 @@ void SymbolLayout::addFeature(const std::size_t index, const SymbolFeature& feature, const std::pair<Shaping, Shaping>& shapedTextOrientations, optional<PositionedIcon> shapedIcon, - const GlyphPositions& glyphs) { + const GlyphPositionMap& glyphPositionMap) { const float minScale = 0.5f; const float glyphSize = 24.0f; const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature); const float layoutIconSize = layout.evaluate<IconSize>(zoom + 1, feature); + const std::array<float, 2> textOffset = layout.evaluate<TextOffset>(zoom, feature); + const std::array<float, 2> iconOffset = layout.evaluate<IconOffset>(zoom, feature); // To reduce the number of labels that jump around when zooming we need // to use a text-size value that is the same for all zoom levels. @@ -318,8 +292,9 @@ void SymbolLayout::addFeature(const std::size_t index, ? SymbolPlacementType::Point : layout.get<SymbolPlacement>(); const float textRepeatDistance = symbolSpacing / 2; - IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, bucketName, symbolInstances.size()}; - + IndexedSubfeature indexedFeature = { feature.index, sourceLayer->getName(), bucketName, + symbolInstances.size() }; + auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers // +-------------------+ Symbols with anchors located on tile edges @@ -344,9 +319,9 @@ void SymbolLayout::addFeature(const std::size_t index, symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout.evaluate(zoom, feature), layoutTextSize, addToBuffers, symbolInstances.size(), - textBoxScale, textPadding, textPlacement, - iconBoxScale, iconPadding, iconPlacement, - glyphs, indexedFeature, index); + textBoxScale, textPadding, textPlacement, textOffset, + iconBoxScale, iconPadding, iconPlacement, iconOffset, + glyphPositionMap, indexedFeature, index); }; const auto& type = feature.getType(); @@ -444,8 +419,8 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) const float cos = std::cos(collisionTile.config.angle); std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) { - const int32_t aRotated = sin * a.point.x + cos * a.point.y; - const int32_t bRotated = sin * b.point.x + cos * b.point.y; + const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y; + const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y; return aRotated != bRotated ? aRotated < bRotated : a.index > b.index; @@ -490,10 +465,21 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f); collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get<TextIgnorePlacement>()); if (glyphScale < collisionTile.maxScale) { + + const float labelAngle = std::fmod((symbolInstance.anchor.angle + collisionTile.config.angle) + 2 * M_PI, 2 * M_PI); + const bool inVerticalRange = ( + (labelAngle > M_PI * 1.0 / 4.0 && labelAngle <= M_PI * 3.0 / 4) || + (labelAngle > M_PI * 5.0 / 4.0 && labelAngle <= M_PI * 7.0 / 4)); + const bool useVerticalMode = symbolInstance.writingModes & WritingModeType::Vertical && inVerticalRange; + + const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature); + bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, + symbolInstance.textOffset, placementZoom, useVerticalMode, symbolInstance.line); + for (const auto& symbol : symbolInstance.glyphQuads) { addSymbol( - bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom, - keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes); + bucket->text, sizeData, symbol, placementZoom, + keepUpright, textPlacement, symbolInstance.anchor, bucket->text.placedSymbols.back()); } } } @@ -502,9 +488,12 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) const float placementZoom = util::max(util::log2(iconScale) + zoom, 0.0f); collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>()); if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) { + const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature); + bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, + symbolInstance.iconOffset, placementZoom, false, symbolInstance.line); addSymbol( - bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom, - keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes); + bucket->icon, sizeData, *symbolInstance.iconQuad, placementZoom, + keepUpright, iconPlacement, symbolInstance.anchor, bucket->icon.placedSymbols.back()); } } @@ -523,14 +512,13 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) template <typename Buffer> void SymbolLayout::addSymbol(Buffer& buffer, - SymbolSizeBinder& sizeBinder, + const Range<float> sizeData, const SymbolQuad& symbol, - const SymbolFeature& feature, const float placementZoom, const bool keepUpright, const style::SymbolPlacementType placement, - const float placementAngle, - const WritingModeType writingModes) { + const Anchor& labelAnchor, + PlacedSymbol& placedSymbol) { constexpr const uint16_t vertexLength = 4; const auto &tl = symbol.tl; @@ -539,30 +527,9 @@ void SymbolLayout::addSymbol(Buffer& buffer, const auto &br = symbol.br; const auto &tex = symbol.tex; - float minZoom = util::max(zoom + util::log2(symbol.minScale), placementZoom); - float maxZoom = util::min(zoom + util::log2(symbol.maxScale), util::MAX_ZOOM_F); - const auto &anchorPoint = symbol.anchorPoint; - - // drop incorrectly oriented glyphs - const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2); - if (writingModes & WritingModeType::Vertical) { - if (placement == style::SymbolPlacementType::Line && symbol.writingMode == WritingModeType::Vertical) { - if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 5 / 4) || a > (M_PI * 7 / 4))) - return; - } else if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 3 / 4) || a > (M_PI * 5 / 4))) - return; - } else if (keepUpright && placement == style::SymbolPlacementType::Line && - (a <= M_PI / 2 || a > M_PI * 3 / 2)) { - return; - } - - if (maxZoom <= minZoom) - return; - - // Lower min zoom so that while fading out the label - // it can be shown outside of collision-free zoom levels - if (minZoom == placementZoom) { - minZoom = 0; + if (placement == style::SymbolPlacementType::Line && keepUpright) { + // drop incorrectly oriented glyphs + if ((symbol.writingMode == WritingModeType::Vertical) != placedSymbol.useVerticalMode) return; } if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { @@ -575,20 +542,17 @@ void SymbolLayout::addSymbol(Buffer& buffer, assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max()); uint16_t index = segment.vertexLength; - // Encode angle of glyph - uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256); - // coordinates (2 triangles) - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tl, tex.x, tex.y, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, tex.x + tex.w, tex.y, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, tex.x, tex.y + tex.h, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, tex.x + tex.w, tex.y + tex.h, - minZoom, maxZoom, placementZoom, glyphAngle)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tl, symbol.glyphOffset.y, tex.x, tex.y, sizeData)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tr, symbol.glyphOffset.y, tex.x + tex.w, tex.y, sizeData)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, bl, symbol.glyphOffset.y, tex.x, tex.y + tex.h, sizeData)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, br, symbol.glyphOffset.y, tex.x + tex.w, tex.y + tex.h, sizeData)); - sizeBinder.populateVertexVector(feature); + auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0, placementZoom); + buffer.dynamicVertices.emplace_back(dynamicVertex); + buffer.dynamicVertices.emplace_back(dynamicVertex); + buffer.dynamicVertices.emplace_back(dynamicVertex); + buffer.dynamicVertices.emplace_back(dynamicVertex); // add the two triangles, referencing the four coordinates we just inserted. buffer.triangles.emplace_back(index + 0, index + 1, index + 2); @@ -596,6 +560,8 @@ void SymbolLayout::addSymbol(Buffer& buffer, segment.vertexLength += vertexLength; segment.indexLength += 6; + + placedSymbol.glyphOffsets.push_back(symbol.glyphOffset.x); } void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) { @@ -635,10 +601,10 @@ void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& auto& segment = collisionBox.segments.back(); uint16_t index = segment.vertexLength; - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, tl, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, tr, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, br, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, bl, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br, maxZoom, placementZoom)); + collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl, maxZoom, placementZoom)); collisionBox.lines.emplace_back(index + 0, index + 1); collisionBox.lines.emplace_back(index + 1, index + 2); diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 7d19aba671..90f5b3c91d 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -20,6 +20,7 @@ class CollisionTile; class SymbolBucket; class Anchor; class RenderLayer; +class PlacedSymbol; namespace style { class Filter; @@ -29,32 +30,26 @@ class SymbolLayout { public: SymbolLayout(const BucketParameters&, const std::vector<const RenderLayer*>&, - const GeometryTileLayer&, - IconDependencies&, + std::unique_ptr<GeometryTileLayer>, + ImageDependencies&, GlyphDependencies&); - void prepare(const GlyphPositionMap& glyphs, const IconMap& icons); + void prepare(const GlyphMap&, const GlyphPositions&, + const ImageMap&, const ImagePositions&); std::unique_ptr<SymbolBucket> place(CollisionTile&); bool hasSymbolInstances() const; - enum State { - Pending, // Waiting for the necessary glyphs or icons to be available. - Placed // The final positions have been determined, taking into account prior layers. - }; - - State state = Pending; - std::map<std::string, - std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>> layerPaintProperties; + std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties; private: void addFeature(const size_t, const SymbolFeature&, const std::pair<Shaping, Shaping>& shapedTextOrientations, optional<PositionedIcon> shapedIcon, - const GlyphPositions& face); + const GlyphPositionMap&); bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&); std::map<std::u16string, std::vector<Anchor>> compareText; @@ -64,20 +59,22 @@ private: // Adds placed items to the buffer. template <typename Buffer> void addSymbol(Buffer&, - SymbolSizeBinder& sizeBinder, + const Range<float> sizeData, const SymbolQuad&, - const SymbolFeature& feature, float scale, const bool keepUpright, const style::SymbolPlacementType, - const float placementAngle, - WritingModeType writingModes); + const Anchor& labelAnchor, + PlacedSymbol& placedSymbol); - const std::string sourceLayerName; + // Stores the layer so that we can hold on to GeometryTileFeature instances in SymbolFeature, + // which may reference data from this object. + const std::unique_ptr<GeometryTileLayer> sourceLayer; const std::string bucketName; const float overscaling; const float zoom; const MapMode mode; + const float pixelRatio; style::SymbolLayoutProperties::PossiblyEvaluated layout; diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp new file mode 100644 index 0000000000..279d251f8f --- /dev/null +++ b/src/mbgl/layout/symbol_projection.cpp @@ -0,0 +1,358 @@ +#include <mbgl/layout/symbol_projection.hpp> +#include <mbgl/map/transform_state.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/frame_history.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/math.hpp> + +namespace mbgl { + + /* + * # Overview of coordinate spaces + * + * ## Tile coordinate spaces + * Each label has an anchor. Some labels have corresponding line geometries. + * The points for both anchors and lines are stored in tile units. Each tile has it's own + * coordinate space going from (0, 0) at the top left to (EXTENT, EXTENT) at the bottom right. + * + * ## GL coordinate space + * At the end of everything, the vertex shader needs to produce a position in GL coordinate space, + * which is (-1, 1) at the top left and (1, -1) in the bottom right. + * + * ## Map pixel coordinate spaces + * Each tile has a pixel coordinate space. It's just the tile units scaled so that one unit is + * whatever counts as 1 pixel at the current zoom. + * This space is used for pitch-alignment=map, rotation-alignment=map + * + * ## Rotated map pixel coordinate spaces + * Like the above, but rotated so axis of the space are aligned with the viewport instead of the tile. + * This space is used for pitch-alignment=map, rotation-alignment=viewport + * + * ## Viewport pixel coordinate space + * (0, 0) is at the top left of the canvas and (pixelWidth, pixelHeight) is at the bottom right corner + * of the canvas. This space is used for pitch-alignment=viewport + * + * + * # Vertex projection + * It goes roughly like this: + * 1. project the anchor and line from tile units into the correct label coordinate space + * - map pixel space pitch-alignment=map rotation-alignment=map + * - rotated map pixel space pitch-alignment=map rotation-alignment=viewport + * - viewport pixel space pitch-alignment=viewport rotation-alignment=* + * 2. if the label follows a line, find the point along the line that is the correct distance from the anchor. + * 3. add the glyph's corner offset to the point from step 3 + * 4. convert from the label coordinate space to gl coordinates + * + * For horizontal labels we want to do step 1 in the shader for performance reasons (no cpu work). + * This is what `u_label_plane_matrix` is used for. + * For labels aligned with lines we have to steps 1 and 2 on the cpu since we need access to the line geometry. + * This is what `updateLineLabels(...)` does. + * Since the conversion is handled on the cpu we just set `u_label_plane_matrix` to an identity matrix. + * + * Steps 3 and 4 are done in the shaders for all labels. + */ + + /* + * Returns a matrix for converting from tile units to the correct label coordinate space. + */ + mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) { + mat4 m; + matrix::identity(m); + if (pitchWithMap) { + matrix::scale(m, m, 1 / pixelsToTileUnits, 1 / pixelsToTileUnits, 1); + if (!rotateWithMap) { + matrix::rotate_z(m, m, state.getAngle()); + } + } else { + matrix::scale(m, m, state.getSize().width / 2.0, -(state.getSize().height / 2.0), 1.0); + matrix::translate(m, m, 1, -1, 0); + matrix::multiply(m, m, posMatrix); + } + return m; + } + + /* + * Returns a matrix for converting from the correct label coordinate space to gl coords. + */ + mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) { + mat4 m; + matrix::identity(m); + if (pitchWithMap) { + matrix::multiply(m, m, posMatrix); + matrix::scale(m, m, pixelsToTileUnits, pixelsToTileUnits, 1); + if (!rotateWithMap) { + matrix::rotate_z(m, m, -state.getAngle()); + } + } else { + matrix::scale(m, m, 1, -1, 1); + matrix::translate(m, m, -1, -1, 0); + matrix::scale(m, m, 2.0 / state.getSize().width, 2.0 / state.getSize().height, 1.0); + } + return m; + } + + + typedef std::pair<Point<float>,float> PointAndCameraDistance; + + PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) { + vec4 pos = {{ point.x, point.y, 0, 1 }}; + matrix::transformMat4(pos, pos, matrix); + return {{ static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) }, pos[3] }; + } + + float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol) { + if (zoomEvaluatedSize.isFeatureConstant) { + return zoomEvaluatedSize.size; + } else { + if (zoomEvaluatedSize.isZoomConstant) { + return placedSymbol.lowerSize; + } else { + return placedSymbol.lowerSize + zoomEvaluatedSize.sizeT * (placedSymbol.upperSize - placedSymbol.lowerSize); + } + } + } + + bool isVisible(const vec4& anchorPos, const float placementZoom, const std::array<double, 2>& clippingBuffer, const FrameHistory& frameHistory) { + const float x = anchorPos[0] / anchorPos[3]; + const float y = anchorPos[1] / anchorPos[3]; + const bool inPaddedViewport = ( + x >= -clippingBuffer[0] && + x <= clippingBuffer[0] && + y >= -clippingBuffer[1] && + y <= clippingBuffer[1]); + return inPaddedViewport && frameHistory.isVisible(placementZoom); + } + + void addDynamicAttributes(const Point<float>& anchorPoint, const float angle, const float placementZoom, + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) { + auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle, placementZoom); + dynamicVertexArray.emplace_back(dynamicVertex); + dynamicVertexArray.emplace_back(dynamicVertex); + dynamicVertexArray.emplace_back(dynamicVertex); + dynamicVertexArray.emplace_back(dynamicVertex); + } + + void hideGlyphs(size_t numGlyphs, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) { + const Point<float> offscreenPoint = { -INFINITY, -INFINITY }; + for (size_t i = 0; i < numGlyphs; i++) { + addDynamicAttributes(offscreenPoint, 0, 25, dynamicVertexArray); + } + } + + struct PlacedGlyph { + PlacedGlyph(Point<float> point_, float angle_) : point(point_), angle(angle_) {} + Point<float> point; + float angle; + }; + + enum PlacementResult { + OK, + NotEnoughRoom, + NeedsFlipping + }; + + Point<float> projectTruncatedLineSegment(const Point<float>& previousTilePoint, const Point<float>& currentTilePoint, const Point<float>& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) { + // We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane + // If it did, that would mean our label extended all the way out from within the viewport to a (very distant) + // point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the + // plane of the camera. + const Point<float> projectedUnitVertex = project(previousTilePoint + util::unit<float>(previousTilePoint - currentTilePoint), projectionMatrix).first; + const Point<float> projectedUnitSegment = previousProjectedPoint - projectedUnitVertex; + + return previousProjectedPoint + (projectedUnitSegment * (minimumLength / util::mag<float>(projectedUnitSegment))); + } + + optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip, + const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) { + + const float combinedOffsetX = flip ? + offsetX - lineOffsetX : + offsetX + lineOffsetX; + + int16_t dir = combinedOffsetX > 0 ? 1 : -1; + + float angle = 0.0; + if (flip) { + // The label needs to be flipped to keep text upright. + // Iterate in the reverse direction. + dir *= -1; + angle = M_PI; + } + + if (dir < 0) angle += M_PI; + + int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1; + + Point<float> current = projectedAnchorPoint; + Point<float> prev = projectedAnchorPoint; + float distanceToPrev = 0.0; + float currentSegmentDistance = 0.0; + const float absOffsetX = std::abs(combinedOffsetX); + + while (distanceToPrev + currentSegmentDistance <= absOffsetX) { + currentIndex += dir; + + // offset does not fit on the projected line + if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) return {}; + + prev = current; + PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix); + if (projection.second > 0) { + current = projection.first; + } else { + // The vertex is behind the plane of the camera, so we can't project it + // Instead, we'll create a vertex along the line that's far enough to include the glyph + const Point<float> previousTilePoint = distanceToPrev == 0 ? + tileAnchorPoint : + convertPoint<float>(line.at(currentIndex - dir)); + const Point<float> currentTilePoint = convertPoint<float>(line.at(currentIndex)); + current = projectTruncatedLineSegment(previousTilePoint, currentTilePoint, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix); + } + + distanceToPrev += currentSegmentDistance; + currentSegmentDistance = util::dist<float>(prev, current); + } + + // The point is on the current segment. Interpolate to find it. + const float segmentInterpolationT = (absOffsetX - distanceToPrev) / currentSegmentDistance; + const Point<float> prevToCurrent = current - prev; + Point<float> p = (prevToCurrent * segmentInterpolationT) + prev; + + // offset the point from the line to text-offset and icon-offset + p += util::perp(prevToCurrent) * static_cast<float>(lineOffsetY * dir / util::mag(prevToCurrent)); + + const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x); + + return {{ p, segmentAngle }}; + } + + PlacementResult placeGlyphsAlongLine(const PlacedSymbol& symbol, + const float fontSize, + const bool flip, + const bool keepUpright, + const mat4& posMatrix, + const mat4& labelPlaneMatrix, + const mat4& glCoordMatrix, + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, + const Point<float>& projectedAnchorPoint) { + const float fontScale = fontSize / 24.0; + const float lineOffsetX = symbol.lineOffset[0] * fontSize; + const float lineOffsetY = symbol.lineOffset[1] * fontSize; + + std::vector<PlacedGlyph> placedGlyphs; + if (symbol.glyphOffsets.size() > 1) { + + const float firstGlyphOffset = symbol.glyphOffsets.front(); + const float lastGlyphOffset = symbol.glyphOffsets.back(); + + optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + if (!firstPlacedGlyph) + return PlacementResult::NotEnoughRoom; + + optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + if (!lastPlacedGlyph) + return PlacementResult::NotEnoughRoom; + + const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix).first; + const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix).first; + + if (keepUpright && !flip && + (symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) { + return PlacementResult::NeedsFlipping; + } + + placedGlyphs.push_back(*firstPlacedGlyph); + for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) { + const float glyphOffsetX = symbol.glyphOffsets[glyphIndex]; + // Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed + auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + placedGlyphs.push_back(*placedGlyph); + } + placedGlyphs.push_back(*lastPlacedGlyph); + } else { + // Only a single glyph to place + // So, determine whether to flip based on projected angle of the line segment it's on + if (keepUpright && !flip) { + const Point<float> a = project(symbol.anchorPoint, posMatrix).first; + const Point<float> tileSegmentEnd = convertPoint<float>(symbol.line.at(symbol.segment + 1)); + const PointAndCameraDistance projectedVertex = project(tileSegmentEnd, posMatrix); + // We know the anchor will be in the viewport, but the end of the line segment may be + // behind the plane of the camera, in which case we can use a point at any arbitrary (closer) + // point on the segment. + const Point<float> b = (projectedVertex.second > 0) ? + projectedVertex.first : + projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix); + + if (symbol.useVerticalMode ? b.y > a.y : b.x < a.x) { + return PlacementResult::NeedsFlipping; + } + } + assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all + const float glyphOffsetX = symbol.glyphOffsets.front(); + optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, + symbol.line, labelPlaneMatrix); + if (!singleGlyph) + return PlacementResult::NotEnoughRoom; + + placedGlyphs.push_back(*singleGlyph); + } + + for (auto& placedGlyph : placedGlyphs) { + addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, dynamicVertexArray); + } + + return PlacementResult::OK; + } + + + void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols, + const mat4& posMatrix, const style::SymbolPropertyValues& values, + const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state, const FrameHistory& frameHistory) { + + const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom()); + + const std::array<double, 2> clippingBuffer = {{ 256.0 / state.getSize().width * 2.0 + 1.0, 256.0 / state.getSize().height * 2.0 + 1.0 }}; + + const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map; + const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map; + const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1, state.getZoom()); + + const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, pitchWithMap, + rotateWithMap, state, pixelsToTileUnits); + + const mat4 glCoordMatrix = getGlCoordMatrix(posMatrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits); + + dynamicVertexArray.clear(); + + for (auto& placedSymbol : placedSymbols) { + vec4 anchorPos = {{ placedSymbol.anchorPoint.x, placedSymbol.anchorPoint.y, 0, 1 }}; + matrix::transformMat4(anchorPos, anchorPos, posMatrix); + + // Don't bother calculating the correct point for invisible labels. + if (!isVisible(anchorPos, placedSymbol.placementZoom, clippingBuffer, frameHistory)) { + hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); + continue; + } + + const float cameraToAnchorDistance = anchorPos[3]; + const float perspectiveRatio = 1 + 0.5 * ((cameraToAnchorDistance / state.getCameraToCenterDistance()) - 1.0); + + const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol); + const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ? + fontSize * perspectiveRatio : + fontSize / perspectiveRatio; + + const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first; + + PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint); + + if (placeUnflipped == PlacementResult::NotEnoughRoom || + (placeUnflipped == PlacementResult::NeedsFlipping && + placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint) == PlacementResult::NotEnoughRoom)) { + hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); + } + } + } +} // end namespace mbgl diff --git a/src/mbgl/layout/symbol_projection.hpp b/src/mbgl/layout/symbol_projection.hpp new file mode 100644 index 0000000000..2652fe7ace --- /dev/null +++ b/src/mbgl/layout/symbol_projection.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include <mbgl/util/mat4.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/programs/symbol_program.hpp> + +namespace mbgl { + + class TransformState; + class RenderTile; + class FrameHistory; + class SymbolSizeBinder; + class PlacedSymbol; + namespace style { + class SymbolPropertyValues; + } // end namespace style + + mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits); + mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits); + + void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>&, const std::vector<PlacedSymbol>&, + const mat4& posMatrix, const style::SymbolPropertyValues&, + const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&, const FrameHistory& frameHistory); + +} // end namespace mbgl diff --git a/src/mbgl/map/backend.cpp b/src/mbgl/map/backend.cpp deleted file mode 100644 index 0b4fd01050..0000000000 --- a/src/mbgl/map/backend.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include <mbgl/map/backend.hpp> -#include <mbgl/map/backend_scope.hpp> -#include <mbgl/gl/context.hpp> -#include <mbgl/gl/extension.hpp> -#include <mbgl/gl/debugging.hpp> - -#include <cassert> - -namespace mbgl { - -Backend::Backend() = default; - -gl::Context& Backend::getContext() { - assert(BackendScope::exists()); - std::call_once(initialized, [this] { - context = std::make_unique<gl::Context>(); - context->enableDebugging(); - context->initializeExtensions( - std::bind(&Backend::initializeExtension, this, std::placeholders::_1)); - }); - return *context; -} - -PremultipliedImage Backend::readFramebuffer(const Size& size) const { - assert(context); - return context->readFramebuffer<PremultipliedImage>(size); -} - -void Backend::assumeFramebufferBinding(const gl::FramebufferID fbo) { - getContext().bindFramebuffer.setCurrentValue(fbo); - if (fbo != ImplicitFramebufferBinding) { - assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); - } -} -void Backend::assumeViewportSize(const Size& size) { - getContext().viewport.setCurrentValue({ 0, 0, size }); - assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue()); -} - -bool Backend::implicitFramebufferBound() { - return getContext().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding; -} - -void Backend::setFramebufferBinding(const gl::FramebufferID fbo) { - getContext().bindFramebuffer = fbo; - if (fbo != ImplicitFramebufferBinding) { - assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); - } -} - -void Backend::setViewportSize(const Size& size) { - getContext().viewport = { 0, 0, size }; - assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue()); -} - -Backend::~Backend() = default; - -} // namespace mbgl diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 35457f3a5b..7534fe67ad 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -1,180 +1,149 @@ #include <mbgl/map/map.hpp> #include <mbgl/map/camera.hpp> -#include <mbgl/map/view.hpp> -#include <mbgl/map/backend.hpp> -#include <mbgl/map/backend_scope.hpp> #include <mbgl/map/transform.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/annotation/annotation_manager.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/style/source.hpp> -#include <mbgl/style/layer.hpp> -#include <mbgl/style/light.hpp> +#include <mbgl/style/style_impl.hpp> #include <mbgl/style/observer.hpp> -#include <mbgl/style/transition_options.hpp> #include <mbgl/renderer/update_parameters.hpp> -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/render_source.hpp> +#include <mbgl/renderer/renderer_frontend.hpp> +#include <mbgl/renderer/renderer_observer.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> -#include <mbgl/util/exception.hpp> +#include <mbgl/util/constants.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/exception.hpp> -#include <mbgl/util/async_task.hpp> #include <mbgl/util/mapbox.hpp> #include <mbgl/util/tile_coordinate.hpp> #include <mbgl/actor/scheduler.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/math/log2.hpp> +#include <utility> namespace mbgl { using namespace style; -enum class RenderState : uint8_t { - Never, - Partial, - Fully, -}; - struct StillImageRequest { - StillImageRequest(View& view_, Map::StillImageCallback&& callback_) - : view(view_), callback(std::move(callback_)) { + StillImageRequest(Map::StillImageCallback&& callback_) + : callback(std::move(callback_)) { } - View& view; Map::StillImageCallback callback; }; -class Map::Impl : public style::Observer { +class Map::Impl : public style::Observer, + public RendererObserver { public: Impl(Map&, - Backend&, + RendererFrontend&, + MapObserver&, float pixelRatio, FileSource&, Scheduler&, MapMode, - GLContextMode, ConstrainMode, - ViewportMode, - optional<std::string> programCacheDir); + ViewportMode); + + ~Impl(); + // StyleObserver void onSourceChanged(style::Source&) override; - void onUpdate(Update) override; + void onUpdate() override; + void onStyleLoading() override; void onStyleLoaded() override; void onStyleError(std::exception_ptr) override; - void onResourceError(std::exception_ptr) override; - - void render(View&); - void renderStill(); - void loadStyleJSON(const std::string&); + // RendererObserver + void onInvalidate() override; + void onResourceError(std::exception_ptr) override; + void onWillStartRenderingFrame() override; + void onDidFinishRenderingFrame(RenderMode, bool) override; + void onWillStartRenderingMap() override; + void onDidFinishRenderingMap() override; Map& map; MapObserver& observer; - Backend& backend; + RendererFrontend& rendererFrontend; FileSource& fileSource; Scheduler& scheduler; - RenderState renderState = RenderState::Never; Transform transform; const MapMode mode; - const GLContextMode contextMode; const float pixelRatio; - const optional<std::string> programCacheDir; MapDebugOptions debugOptions { MapDebugOptions::NoDebug }; - Update updateFlags = Update::Nothing; - - std::unique_ptr<AnnotationManager> annotationManager; - std::unique_ptr<Painter> painter; std::unique_ptr<Style> style; + AnnotationManager annotationManager; - std::string styleURL; - std::string styleJSON; - bool styleMutated = false; bool cameraMutated = false; - std::unique_ptr<AsyncRequest> styleRequest; + uint8_t prefetchZoomDelta = util::DEFAULT_PREFETCH_ZOOM_DELTA; - size_t sourceCacheSize; bool loading = false; - - util::AsyncTask asyncInvalidate; + bool rendererFullyLoaded; std::unique_ptr<StillImageRequest> stillImageRequest; }; -Map::Map(Backend& backend, +Map::Map(RendererFrontend& rendererFrontend, + MapObserver& mapObserver, const Size size, const float pixelRatio, FileSource& fileSource, Scheduler& scheduler, MapMode mapMode, - GLContextMode contextMode, ConstrainMode constrainMode, - ViewportMode viewportMode, - const optional<std::string>& programCacheDir) + ViewportMode viewportMode) : impl(std::make_unique<Impl>(*this, - backend, + rendererFrontend, + mapObserver, pixelRatio, fileSource, scheduler, mapMode, - contextMode, constrainMode, - viewportMode, - programCacheDir)) { + viewportMode)) { impl->transform.resize(size); } Map::Impl::Impl(Map& map_, - Backend& backend_, + RendererFrontend& frontend, + MapObserver& mapObserver, float pixelRatio_, FileSource& fileSource_, Scheduler& scheduler_, MapMode mode_, - GLContextMode contextMode_, ConstrainMode constrainMode_, - ViewportMode viewportMode_, - optional<std::string> programCacheDir_) + ViewportMode viewportMode_) : map(map_), - observer(backend_), - backend(backend_), + observer(mapObserver), + rendererFrontend(frontend), fileSource(fileSource_), scheduler(scheduler_), transform(observer, constrainMode_, viewportMode_), mode(mode_), - contextMode(contextMode_), pixelRatio(pixelRatio_), - programCacheDir(programCacheDir_), - annotationManager(std::make_unique<AnnotationManager>(pixelRatio)), - asyncInvalidate([this] { - if (mode == MapMode::Continuous) { - backend.invalidate(); - } else { - renderStill(); - } - }) { -} + style(std::make_unique<Style>(scheduler, fileSource, pixelRatio)), + annotationManager(*style) { -Map::~Map() { - BackendScope guard(impl->backend); + style->impl->setObserver(this); + rendererFrontend.setObserver(*this); +} - impl->styleRequest = nullptr; +Map::Impl::~Impl() { + // Explicitly reset the RendererFrontend first to ensure it releases + // All shared resources (AnnotationManager) + rendererFrontend.reset(); +}; - // Explicit resets currently necessary because these abandon resources that need to be - // cleaned up by context.reset(); - impl->style.reset(); - impl->annotationManager.reset(); - impl->painter.reset(); -} +Map::~Map() = default; -void Map::renderStill(View& view, StillImageCallback callback) { +void Map::renderStill(StillImageCallback callback) { if (!callback) { Log::Error(Event::General, "StillImageCallback not set"); return; @@ -190,238 +159,93 @@ void Map::renderStill(View& view, StillImageCallback callback) { return; } - if (!impl->style) { - callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style"))); + if (impl->style->impl->getLastError()) { + callback(impl->style->impl->getLastError()); return; } - if (impl->style->getLastError()) { - callback(impl->style->getLastError()); - return; - } + impl->stillImageRequest = std::make_unique<StillImageRequest>(std::move(callback)); - impl->stillImageRequest = std::make_unique<StillImageRequest>(view, std::move(callback)); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } -void Map::Impl::renderStill() { - if (!stillImageRequest) { - return; - } - - // TODO: determine whether we need activate/deactivate - BackendScope guard(backend); - render(stillImageRequest->view); +void Map::renderStill(const CameraOptions& camera, MapDebugOptions debugOptions, StillImageCallback callback) { + impl->cameraMutated = true; + impl->debugOptions = debugOptions; + impl->transform.jumpTo(camera); + renderStill(std::move(callback)); } void Map::triggerRepaint() { - impl->backend.invalidate(); -} - -void Map::render(View& view) { - impl->render(view); + impl->onUpdate(); } -void Map::Impl::render(View& view) { - if (!style) { - return; - } - - TimePoint timePoint = mode == MapMode::Continuous - ? Clock::now() - : Clock::time_point::max(); - - transform.updateTransitions(timePoint); - - if (style->loaded && updateFlags & Update::AnnotationStyle) { - annotationManager->updateStyle(*style); - } - - if (updateFlags & Update::AnnotationData) { - annotationManager->updateData(); - } - - style->update({ - mode, - updateFlags, - pixelRatio, - debugOptions, - timePoint, - transform.getState(), - scheduler, - fileSource, - *annotationManager - }); +#pragma mark - Map::Impl RendererObserver - updateFlags = Update::Nothing; - - gl::Context& context = backend.getContext(); - if (!painter) { - painter = std::make_unique<Painter>(context, transform.getState(), pixelRatio, programCacheDir); +void Map::Impl::onWillStartRenderingMap() { + if (mode == MapMode::Continuous) { + observer.onWillStartRenderingMap(); } +} +void Map::Impl::onWillStartRenderingFrame() { if (mode == MapMode::Continuous) { - if (renderState == RenderState::Never) { - observer.onWillStartRenderingMap(); - } - observer.onWillStartRenderingFrame(); + } +} - FrameData frameData { timePoint, - pixelRatio, - mode, - contextMode, - debugOptions }; - - backend.updateAssumedState(); - - painter->render(*style, - frameData, - view, - annotationManager->getSpriteAtlas()); - - painter->cleanup(); - - observer.onDidFinishRenderingFrame(style->isLoaded() ? MapObserver::RenderMode::Full : MapObserver::RenderMode::Partial); +void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepaint) { + rendererFullyLoaded = renderMode == RenderMode::Full; - if (!style->isLoaded()) { - renderState = RenderState::Partial; - } else if (renderState != RenderState::Fully) { - renderState = RenderState::Fully; - observer.onDidFinishRenderingMap(MapObserver::RenderMode::Full); - if (loading) { - loading = false; - observer.onDidFinishLoadingMap(); - } - } + if (mode == MapMode::Continuous) { + observer.onDidFinishRenderingFrame(MapObserver::RenderMode(renderMode)); - // Schedule an update if we need to paint another frame due to transitions or - // animations that are still in progress - if (style->hasTransitions() || painter->needsAnimation() || transform.inTransition()) { - onUpdate(Update::Repaint); + if (needsRepaint || transform.inTransition()) { + onUpdate(); } - } else if (stillImageRequest && style->isLoaded()) { - FrameData frameData { timePoint, - pixelRatio, - mode, - contextMode, - debugOptions }; - - backend.updateAssumedState(); - - painter->render(*style, - frameData, - view, - annotationManager->getSpriteAtlas()); - + } else if (stillImageRequest && rendererFullyLoaded) { auto request = std::move(stillImageRequest); request->callback(nullptr); - - painter->cleanup(); } } -#pragma mark - Style - -void Map::setStyleURL(const std::string& url) { - if (impl->styleURL == url) { - return; - } - - impl->loading = true; - - impl->observer.onWillStartLoadingMap(); - - impl->styleRequest = nullptr; - impl->styleURL = url; - impl->styleJSON.clear(); - impl->styleMutated = false; - - impl->style = std::make_unique<Style>(impl->scheduler, impl->fileSource, impl->pixelRatio); - - impl->styleRequest = impl->fileSource.request(Resource::style(impl->styleURL), [this](Response res) { - // Once we get a fresh style, or the style is mutated, stop revalidating. - if (res.isFresh() || impl->styleMutated) { - impl->styleRequest.reset(); - } - - // Don't allow a loaded, mutated style to be overwritten with a new version. - if (impl->styleMutated && impl->style->loaded) { - return; +void Map::Impl::onDidFinishRenderingMap() { + if (mode == MapMode::Continuous && loading) { + observer.onDidFinishRenderingMap(MapObserver::RenderMode::Full); + if (loading) { + loading = false; + observer.onDidFinishLoadingMap(); } - - if (res.error) { - if (res.error->reason == Response::Error::Reason::NotFound && - util::mapbox::isMapboxURL(impl->styleURL)) { - const std::string message = "style " + impl->styleURL + " could not be found or is an incompatible legacy map or style"; - Log::Error(Event::Setup, message.c_str()); - impl->onStyleError(std::make_exception_ptr(util::NotFoundException(message))); - } else { - const std::string message = "loading style failed: " + res.error->message; - Log::Error(Event::Setup, message.c_str()); - impl->onStyleError(std::make_exception_ptr(util::StyleLoadException(message))); - } - impl->onResourceError(std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified || res.noContent) { - return; - } else { - impl->loadStyleJSON(*res.data); - } - }); -} - -void Map::setStyleJSON(const std::string& json) { - if (impl->styleJSON == json) { - return; } +}; - impl->loading = true; - - impl->observer.onWillStartLoadingMap(); - - impl->styleURL.clear(); - impl->styleJSON.clear(); - impl->styleMutated = false; - - impl->style = std::make_unique<Style>(impl->scheduler, impl->fileSource, impl->pixelRatio); - - impl->loadStyleJSON(json); -} - -void Map::Impl::loadStyleJSON(const std::string& json) { - style->setObserver(this); - style->setJSON(json); - styleJSON = json; - - if (!cameraMutated) { - // Zoom first because it may constrain subsequent operations. - map.setZoom(map.getDefaultZoom()); - map.setLatLng(map.getDefaultLatLng()); - map.setBearing(map.getDefaultBearing()); - map.setPitch(map.getDefaultPitch()); - } +#pragma mark - Style - onUpdate(Update::Classes | Update::AnnotationStyle); +style::Style& Map::getStyle() { + return *impl->style; } -std::string Map::getStyleURL() const { - return impl->styleURL; +const style::Style& Map::getStyle() const { + return *impl->style; } -std::string Map::getStyleJSON() const { - return impl->styleJSON; +void Map::setStyle(std::unique_ptr<Style> style) { + assert(style); + impl->onStyleLoading(); + impl->style = std::move(style); + impl->annotationManager.setStyle(*impl->style); } #pragma mark - Transitions void Map::cancelTransitions() { impl->transform.cancelTransitions(); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setGestureInProgress(bool inProgress) { impl->transform.setGestureInProgress(inProgress); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } bool Map::isGestureInProgress() const { @@ -449,19 +273,19 @@ CameraOptions Map::getCameraOptions(const EdgeInsets& padding) const { void Map::jumpTo(const CameraOptions& camera) { impl->cameraMutated = true; impl->transform.jumpTo(camera); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.easeTo(camera, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.flyTo(camera, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } #pragma mark - Position @@ -469,7 +293,7 @@ void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) void Map::moveBy(const ScreenCoordinate& point, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.moveBy(point, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) { @@ -480,13 +304,13 @@ void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) { void Map::setLatLng(const LatLng& latLng, const EdgeInsets& padding, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setLatLng(latLng, padding, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setLatLng(latLng, anchor, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } LatLng Map::getLatLng(const EdgeInsets& padding) const { @@ -502,7 +326,7 @@ void Map::resetPosition(const EdgeInsets& padding) { camera.padding = padding; camera.zoom = 0; impl->transform.jumpTo(camera); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } @@ -516,13 +340,13 @@ void Map::setZoom(double zoom, const AnimationOptions& animation) { void Map::setZoom(double zoom, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setZoom(zoom, anchor, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setZoom(double zoom, const EdgeInsets& padding, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setZoom(zoom, padding, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } double Map::getZoom() const { @@ -537,7 +361,7 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const AnimationOption void Map::setLatLngZoom(const LatLng& latLng, double zoom, const EdgeInsets& padding, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setLatLngZoom(latLng, zoom, padding, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding) const { @@ -627,7 +451,7 @@ optional<LatLngBounds> Map::getLatLngBounds() const { void Map::setLatLngBounds(optional<LatLngBounds> bounds) { impl->cameraMutated = true; impl->transform.setLatLngBounds(bounds); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setMinZoom(const double minZoom) { @@ -678,7 +502,7 @@ double Map::getMaxPitch() const { void Map::setSize(const Size size) { impl->transform.resize(size); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } Size Map::getSize() const { @@ -690,7 +514,7 @@ Size Map::getSize() const { void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.rotateBy(first, second, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setBearing(double degrees, const AnimationOptions& animation) { @@ -701,13 +525,13 @@ void Map::setBearing(double degrees, const AnimationOptions& animation) { void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::setBearing(double degrees, const EdgeInsets& padding, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setAngle(-degrees * util::DEG2RAD, padding, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } double Map::getBearing() const { @@ -717,7 +541,7 @@ double Map::getBearing() const { void Map::resetNorth(const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setAngle(0, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } #pragma mark - Pitch @@ -730,7 +554,7 @@ void Map::setPitch(double pitch, const AnimationOptions& animation) { void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; impl->transform.setPitch(pitch * util::DEG2RAD, anchor, animation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } double Map::getPitch() const { @@ -741,7 +565,7 @@ double Map::getPitch() const { void Map::setNorthOrientation(NorthOrientation orientation) { impl->transform.setNorthOrientation(orientation); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } NorthOrientation Map::getNorthOrientation() const { @@ -752,7 +576,7 @@ NorthOrientation Map::getNorthOrientation() const { void Map::setConstrainMode(mbgl::ConstrainMode mode) { impl->transform.setConstrainMode(mode); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } ConstrainMode Map::getConstrainMode() const { @@ -763,7 +587,7 @@ ConstrainMode Map::getConstrainMode() const { void Map::setViewportMode(mbgl::ViewportMode mode) { impl->transform.setViewportMode(mode); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } ViewportMode Map::getViewportMode() const { @@ -787,239 +611,40 @@ LatLng Map::latLngForPixel(const ScreenCoordinate& pixel) const { #pragma mark - Annotations -void Map::addAnnotationImage(const std::string& id, std::unique_ptr<style::Image> image) { - impl->annotationManager->addImage(id, std::move(image)); +void Map::addAnnotationImage(std::unique_ptr<style::Image> image) { + impl->annotationManager.addImage(std::move(image)); } void Map::removeAnnotationImage(const std::string& id) { - impl->annotationManager->removeImage(id); + impl->annotationManager.removeImage(id); } double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) { - return impl->annotationManager->getTopOffsetPixelsForImage(id); + return impl->annotationManager.getTopOffsetPixelsForImage(id); } AnnotationID Map::addAnnotation(const Annotation& annotation) { - auto result = impl->annotationManager->addAnnotation(annotation, getMaxZoom()); - impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData); + auto result = impl->annotationManager.addAnnotation(annotation, getMaxZoom()); + impl->onUpdate(); return result; } void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) { - impl->onUpdate(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom())); -} - -void Map::removeAnnotation(AnnotationID annotation) { - impl->annotationManager->removeAnnotation(annotation); - impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData); -} - -#pragma mark - Feature query api - -std::vector<Feature> Map::queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options) { - if (!impl->style) return {}; - - return impl->style->queryRenderedFeatures( - { point }, - impl->transform.getState(), - options - ); -} - -std::vector<Feature> Map::queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options) { - if (!impl->style) return {}; - - return impl->style->queryRenderedFeatures( - { - box.min, - { box.max.x, box.min.y }, - box.max, - { box.min.x, box.max.y }, - box.min - }, - impl->transform.getState(), - options - ); -} - -std::vector<Feature> Map::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) { - if (!impl->style) return {}; - - const RenderSource* source = impl->style->getRenderSource(sourceID); - if (!source) return {}; - - return source->querySourceFeatures(options); -} - -AnnotationIDs Map::queryPointAnnotations(const ScreenBox& box) { - RenderedQueryOptions options; - options.layerIDs = {{ AnnotationManager::PointLayerID }}; - auto features = queryRenderedFeatures(box, options); - std::set<AnnotationID> set; - for (auto &feature : features) { - assert(feature.id); - assert(feature.id->is<uint64_t>()); - assert(feature.id->get<uint64_t>() <= std::numeric_limits<AnnotationID>::max()); - set.insert(static_cast<AnnotationID>(feature.id->get<uint64_t>())); + if (impl->annotationManager.updateAnnotation(id, annotation, getMaxZoom())) { + impl->onUpdate(); } - AnnotationIDs ids; - ids.reserve(set.size()); - std::move(set.begin(), set.end(), std::back_inserter(ids)); - return ids; } -#pragma mark - Style API - -std::vector<style::Source*> Map::getSources() { - return impl->style ? impl->style->getSources() : std::vector<style::Source*>(); -} - -style::Source* Map::getSource(const std::string& sourceID) { - if (impl->style) { - impl->styleMutated = true; - return impl->style->getSource(sourceID); - } - return nullptr; -} - -void Map::addSource(std::unique_ptr<style::Source> source) { - if (impl->style) { - impl->styleMutated = true; - impl->style->addSource(std::move(source)); - } -} - -std::unique_ptr<Source> Map::removeSource(const std::string& sourceID) { - if (impl->style) { - impl->styleMutated = true; - return impl->style->removeSource(sourceID); - } - return nullptr; -} - -std::vector<style::Layer*> Map::getLayers() { - return impl->style ? impl->style->getLayers() : std::vector<style::Layer*>(); -} - -Layer* Map::getLayer(const std::string& layerID) { - if (impl->style) { - impl->styleMutated = true; - return impl->style->getLayer(layerID); - } - return nullptr; -} - -void Map::addLayer(std::unique_ptr<Layer> layer, const optional<std::string>& before) { - if (!impl->style) { - return; - } - - impl->styleMutated = true; - BackendScope guard(impl->backend); - - impl->style->addLayer(std::move(layer), before); - impl->onUpdate(Update::Classes); -} - -std::unique_ptr<Layer> Map::removeLayer(const std::string& id) { - if (!impl->style) { - return nullptr; - } - - impl->styleMutated = true; - BackendScope guard(impl->backend); - - auto removedLayer = impl->style->removeLayer(id); - impl->onUpdate(Update::Repaint); - - return removedLayer; -} - -void Map::addImage(const std::string& id, std::unique_ptr<style::Image> image) { - if (!impl->style) { - return; - } - - impl->styleMutated = true; - impl->style->spriteAtlas->addImage(id, std::move(image)); - impl->onUpdate(Update::Repaint); -} - -void Map::removeImage(const std::string& id) { - if (!impl->style) { - return; - } - - impl->styleMutated = true; - impl->style->spriteAtlas->removeImage(id); - impl->onUpdate(Update::Repaint); -} - -const style::Image* Map::getImage(const std::string& id) { - if (impl->style) { - return impl->style->spriteAtlas->getImage(id); - } - return nullptr; -} - -void Map::setLight(std::unique_ptr<style::Light> light) { - if (!impl->style) { - return; - } - - impl->style->setLight(std::move(light)); -} - -style::Light* Map::getLight() { - if (!impl->style) { - return nullptr; - } - - return impl->style->getLight(); -} - -#pragma mark - Defaults - -std::string Map::getStyleName() const { - if (impl->style) { - return impl->style->getName(); - } - return {}; -} - -LatLng Map::getDefaultLatLng() const { - if (impl->style) { - return impl->style->getDefaultLatLng(); - } - return {}; -} - -double Map::getDefaultZoom() const { - if (impl->style) { - return impl->style->getDefaultZoom(); - } - return {}; -} - -double Map::getDefaultBearing() const { - if (impl->style) { - return impl->style->getDefaultBearing(); - } - return {}; -} - -double Map::getDefaultPitch() const { - if (impl->style) { - return impl->style->getDefaultPitch(); - } - return {}; +void Map::removeAnnotation(AnnotationID annotation) { + impl->annotationManager.removeAnnotation(annotation); + impl->onUpdate(); } #pragma mark - Toggles void Map::setDebug(MapDebugOptions debugOptions) { impl->debugOptions = debugOptions; - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } void Map::cycleDebugOptions() { @@ -1043,90 +668,72 @@ void Map::cycleDebugOptions() { else impl->debugOptions = MapDebugOptions::TileBorders; - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } MapDebugOptions Map::getDebug() const { return impl->debugOptions; } -bool Map::isFullyLoaded() const { - return impl->style ? impl->style->isLoaded() : false; -} - -void Map::addClass(const std::string& className) { - if (impl->style && impl->style->addClass(className)) { - impl->onUpdate(Update::Classes); - } -} - -void Map::removeClass(const std::string& className) { - if (impl->style && impl->style->removeClass(className)) { - impl->onUpdate(Update::Classes); - } +void Map::setPrefetchZoomDelta(uint8_t delta) { + impl->prefetchZoomDelta = delta; } -void Map::setClasses(const std::vector<std::string>& classNames) { - if (impl->style) { - impl->style->setClasses(classNames); - impl->onUpdate(Update::Classes); - } +uint8_t Map::getPrefetchZoomDelta() const { + return impl->prefetchZoomDelta; } -style::TransitionOptions Map::getTransitionOptions() const { - if (impl->style) { - return impl->style->getTransitionOptions(); - } - return {}; +bool Map::isFullyLoaded() const { + return impl->style->impl->isLoaded() && impl->rendererFullyLoaded; } -void Map::setTransitionOptions(const style::TransitionOptions& options) { - if (impl->style) { - impl->style->setTransitionOptions(options); - } +void Map::Impl::onSourceChanged(style::Source& source) { + observer.onSourceChanged(source); } -bool Map::hasClass(const std::string& className) const { - return impl->style ? impl->style->hasClass(className) : false; +void Map::Impl::onInvalidate() { + onUpdate(); } -std::vector<std::string> Map::getClasses() const { - if (impl->style) { - return impl->style->getClasses(); - } - return {}; -} +void Map::Impl::onUpdate() { + TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max(); -void Map::setSourceTileCacheSize(size_t size) { - if (size != impl->sourceCacheSize) { - impl->sourceCacheSize = size; - if (!impl->style) return; - impl->style->setSourceTileCacheSize(size); - impl->backend.invalidate(); - } -} + transform.updateTransitions(timePoint); -void Map::onLowMemory() { - if (impl->painter) { - BackendScope guard(impl->backend); - impl->painter->cleanup(); - } - if (impl->style) { - impl->style->onLowMemory(); - impl->backend.invalidate(); - } -} + UpdateParameters params = { + style->impl->isLoaded(), + mode, + pixelRatio, + debugOptions, + timePoint, + transform.getState(), + style->impl->getGlyphURL(), + style->impl->spriteLoaded, + style->impl->getTransitionOptions(), + style->impl->getLight()->impl, + style->impl->getImageImpls(), + style->impl->getSourceImpls(), + style->impl->getLayerImpls(), + annotationManager, + prefetchZoomDelta, + bool(stillImageRequest) + }; -void Map::Impl::onSourceChanged(style::Source& source) { - observer.onSourceChanged(source); + rendererFrontend.update(std::make_shared<UpdateParameters>(std::move(params))); } -void Map::Impl::onUpdate(Update flags) { - updateFlags |= flags; - asyncInvalidate.send(); +void Map::Impl::onStyleLoading() { + loading = true; + rendererFullyLoaded = false; + observer.onWillStartLoadingMap(); } void Map::Impl::onStyleLoaded() { + if (!cameraMutated) { + map.jumpTo(style->getDefaultCamera()); + } + + annotationManager.onStyleLoaded(); observer.onDidFinishLoadingStyle(); } @@ -1143,12 +750,7 @@ void Map::Impl::onResourceError(std::exception_ptr error) { void Map::dumpDebugLogs() const { Log::Info(Event::General, "--------------------------------------------------------------------------------"); - Log::Info(Event::General, "MapContext::styleURL: %s", impl->styleURL.c_str()); - if (impl->style) { - impl->style->dumpDebugLogs(); - } else { - Log::Info(Event::General, "no style loaded"); - } + impl->style->impl->dumpDebugLogs(); Log::Info(Event::General, "--------------------------------------------------------------------------------"); } diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 8d05bc0e91..2bb25af28f 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -1,6 +1,5 @@ #include <mbgl/map/camera.hpp> #include <mbgl/map/transform.hpp> -#include <mbgl/map/view.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/mat4.hpp> #include <mbgl/util/math.hpp> @@ -168,7 +167,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima double angle = camera.angle.value_or(getAngle()); double pitch = camera.pitch.value_or(getPitch()); - if (std::isnan(zoom)) { + if (std::isnan(zoom) || state.size.isEmpty()) { return; } @@ -293,6 +292,11 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima Point<double> framePoint = util::interpolate(startPoint, endPoint, us); double frameZoom = startZoom + state.scaleZoom(1 / w(s)); + // Zoom can be NaN if size is empty. + if (std::isnan(frameZoom)) { + frameZoom = zoom; + } + // Convert to geographic coordinates and set the new viewpoint. LatLng frameLatLng = Projection::unproject(framePoint, startScale); state.setLatLngZoom(frameLatLng, frameZoom); diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index bbf7e22b31..d1a320beae 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -132,7 +132,7 @@ double TransformState::getZoom() const { return scaleZoom(scale); } -int32_t TransformState::getIntegerZoom() const { +uint8_t TransformState::getIntegerZoom() const { return getZoom(); } @@ -358,7 +358,7 @@ void TransformState::setLatLngZoom(const LatLng& latLng, double zoom) { constrained = bounds->constrain(latLng); } - double newScale = zoomScale(zoom); + double newScale = util::clamp(zoomScale(zoom), min_scale, max_scale); const double newWorldSize = newScale * util::tileSize; Bc = newWorldSize / util::DEGREES_MAX; Cc = newWorldSize / util::M2PI; @@ -385,4 +385,16 @@ void TransformState::setScalePoint(const double newScale, const ScreenCoordinate Cc = Projection::worldSize(scale) / util::M2PI; } +float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) const { + mat4 projectionMatrix; + getProjMatrix(projectionMatrix); + mat4 tileProjectionMatrix; + matrixFor(tileProjectionMatrix, tileID); + matrix::multiply(tileProjectionMatrix, projectionMatrix, tileProjectionMatrix); + vec4 tileCenter = {{util::tileSize / 2, util::tileSize / 2, 0, 1}}; + vec4 projectedCenter; + matrix::transformMat4(projectedCenter, tileCenter, tileProjectionMatrix); + return projectedCenter[3]; +} + } // namespace mbgl diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index e6464aeacc..59522d89fd 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -47,7 +47,7 @@ public: // Zoom double getZoom() const; - int32_t getIntegerZoom() const; + uint8_t getIntegerZoom() const; double getZoomFraction() const; // Bounds @@ -86,6 +86,8 @@ public: return !size.isEmpty() && (scale >= min_scale && scale <= max_scale); } + float getCameraToTileDistance(const UnwrappedTileID&) const; + private: bool rotatedNorth() const; void constrain(double& scale, double& x, double& y) const; @@ -94,7 +96,7 @@ private: // Limit the amount of zooming possible on the map. double min_scale = std::pow(2, 0); - double max_scale = std::pow(2, 20); + double max_scale = std::pow(2, util::DEFAULT_MAX_ZOOM); double min_pitch = 0.0; double max_pitch = util::PITCH_MAX; diff --git a/src/mbgl/map/update.hpp b/src/mbgl/map/update.hpp deleted file mode 100644 index 5e87515eac..0000000000 --- a/src/mbgl/map/update.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include <mbgl/util/traits.hpp> - -namespace mbgl { - -enum class Update { - Nothing = 0, - Repaint = 1 << 0, - Classes = 1 << 2, - RecalculateStyle = 1 << 3, - AnnotationStyle = 1 << 6, - AnnotationData = 1 << 7 -}; - -constexpr Update operator|(Update lhs, Update rhs) { - return Update(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs)); -} - -constexpr Update& operator|=(Update& lhs, const Update& rhs) { - return (lhs = lhs | rhs); -} - -constexpr bool operator& (Update lhs, Update rhs) { - return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs); -} - -} // namespace mbgl diff --git a/src/mbgl/map/zoom_history.hpp b/src/mbgl/map/zoom_history.hpp index 308846b1e3..7821499d72 100644 --- a/src/mbgl/map/zoom_history.hpp +++ b/src/mbgl/map/zoom_history.hpp @@ -8,33 +8,39 @@ namespace mbgl { struct ZoomHistory { float lastZoom; + float lastFloorZoom; float lastIntegerZoom; TimePoint lastIntegerZoomTime; bool first = true; bool update(float z, const TimePoint& now) { + constexpr TimePoint zero = TimePoint(Duration::zero()); + const float floorZ = std::floor(z); + if (first) { first = false; - lastIntegerZoom = std::floor(z); - lastIntegerZoomTime = TimePoint(Duration::zero()); + lastIntegerZoom = floorZ; + lastIntegerZoomTime = zero; + lastZoom = z; + lastFloorZoom = floorZ; + return true; + } + + if (lastFloorZoom > floorZ) { + lastIntegerZoom = floorZ + 1; + lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now; + } else if (lastFloorZoom < floorZ) { + lastIntegerZoom = floorZ; + lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now; + } + + if (z != lastZoom) { lastZoom = z; + lastFloorZoom = floorZ; return true; - } else { - if (std::floor(lastZoom) < std::floor(z)) { - lastIntegerZoom = std::floor(z); - lastIntegerZoomTime = now; - } else if (std::floor(lastZoom) > std::floor(z)) { - lastIntegerZoom = std::floor(z + 1); - lastIntegerZoomTime = now; - } - - if (z != lastZoom) { - lastZoom = z; - return true; - } - - return false; } + + return false; } }; diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index 5e3166d082..d023ec7d83 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -24,6 +24,9 @@ MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos); MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude); MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset); MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_normal); +MBGL_DEFINE_ATTRIBUTE(float, 3, a_projected_pos); +MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_label_pos); +MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_anchor_pos); MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos); MBGL_DEFINE_ATTRIBUTE(int16_t, 3, a_normal); MBGL_DEFINE_ATTRIBUTE(uint16_t, 1, a_edgedistance); @@ -97,6 +100,11 @@ struct a_width { using Type = gl::Attribute<float, 1>; }; +struct a_floorwidth { + static auto name() { return "a_floorwidth"; } + using Type = gl::Attribute<float, 1>; +}; + struct a_height { static auto name() { return "a_height"; } using Type = gl::Attribute<float, 1>; diff --git a/src/mbgl/programs/binary_program.cpp b/src/mbgl/programs/binary_program.cpp index 1cad0a2693..da629194b4 100644 --- a/src/mbgl/programs/binary_program.cpp +++ b/src/mbgl/programs/binary_program.cpp @@ -2,6 +2,8 @@ #include <protozero/pbf_reader.hpp> #include <protozero/pbf_writer.hpp> +#include <utility> +#include <stdexcept> template <class Binding> static std::pair<const std::string, Binding> parseBinding(protozero::pbf_reader&& pbf) { @@ -64,12 +66,12 @@ BinaryProgram::BinaryProgram(std::string&& data) { BinaryProgram::BinaryProgram( gl::BinaryProgramFormat binaryFormat_, std::string&& binaryCode_, - const std::string& binaryIdentifier_, + std::string binaryIdentifier_, std::vector<std::pair<const std::string, gl::AttributeLocation>>&& attributes_, std::vector<std::pair<const std::string, gl::UniformLocation>>&& uniforms_) : binaryFormat(binaryFormat_), binaryCode(std::move(binaryCode_)), - binaryIdentifier(binaryIdentifier_), + binaryIdentifier(std::move(binaryIdentifier_)), attributes(std::move(attributes_)), uniforms(std::move(uniforms_)) { } diff --git a/src/mbgl/programs/binary_program.hpp b/src/mbgl/programs/binary_program.hpp index 2806f4fb3e..8690f3fd6f 100644 --- a/src/mbgl/programs/binary_program.hpp +++ b/src/mbgl/programs/binary_program.hpp @@ -15,7 +15,7 @@ public: BinaryProgram(gl::BinaryProgramFormat, std::string&& binaryCode, - const std::string& binaryIdentifier, + std::string binaryIdentifier, std::vector<std::pair<const std::string, gl::AttributeLocation>>&&, std::vector<std::pair<const std::string, gl::UniformLocation>>&&); diff --git a/src/mbgl/programs/circle_program.hpp b/src/mbgl/programs/circle_program.hpp index 8f056048b1..3590acbeef 100644 --- a/src/mbgl/programs/circle_program.hpp +++ b/src/mbgl/programs/circle_program.hpp @@ -21,7 +21,9 @@ class CircleProgram : public Program< gl::Uniforms< uniforms::u_matrix, uniforms::u_scale_with_map, - uniforms::u_extrude_scale>, + uniforms::u_extrude_scale, + uniforms::u_camera_to_center_distance, + uniforms::u_pitch_with_map>, style::CirclePaintProperties> { public: diff --git a/src/mbgl/programs/collision_box_program.cpp b/src/mbgl/programs/collision_box_program.cpp index a3dc01ebe4..57107db75d 100644 --- a/src/mbgl/programs/collision_box_program.cpp +++ b/src/mbgl/programs/collision_box_program.cpp @@ -2,6 +2,6 @@ namespace mbgl { -static_assert(sizeof(CollisionBoxProgram::LayoutVertex) == 10, "expected CollisionBoxVertex size"); +static_assert(sizeof(CollisionBoxProgram::LayoutVertex) == 14, "expected CollisionBoxVertex size"); } // namespace mbgl diff --git a/src/mbgl/programs/collision_box_program.hpp b/src/mbgl/programs/collision_box_program.hpp index 89b69484fd..ba99e0c087 100644 --- a/src/mbgl/programs/collision_box_program.hpp +++ b/src/mbgl/programs/collision_box_program.hpp @@ -4,6 +4,7 @@ #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> #include <mbgl/shaders/collision_box.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/util/geometry.hpp> #include <cmath> @@ -17,6 +18,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom); using CollisionBoxAttributes = gl::Attributes< attributes::a_pos, + attributes::a_anchor_pos, attributes::a_extrude, attributes::a_data<uint8_t, 2>>; @@ -28,19 +30,27 @@ class CollisionBoxProgram : public Program< uniforms::u_matrix, uniforms::u_scale, uniforms::u_zoom, - uniforms::u_maxzoom>, - style::PaintProperties<>> + uniforms::u_maxzoom, + uniforms::u_collision_y_stretch, + uniforms::u_camera_to_center_distance, + uniforms::u_pitch, + uniforms::u_fadetexture>, + style::Properties<>> { public: using Program::Program; - static LayoutVertex vertex(Point<float> a, Point<float> o, float maxzoom, float placementZoom) { + static LayoutVertex vertex(Point<float> a, Point<float> anchor, Point<float> o, float maxzoom, float placementZoom) { return LayoutVertex { {{ static_cast<int16_t>(a.x), static_cast<int16_t>(a.y) }}, {{ + static_cast<int16_t>(anchor.x), + static_cast<int16_t>(anchor.y) + }}, + {{ static_cast<int16_t>(::round(o.x)), static_cast<int16_t>(::round(o.y)) }}, diff --git a/src/mbgl/programs/debug_program.hpp b/src/mbgl/programs/debug_program.hpp index de1666b4a8..7a6d075cdb 100644 --- a/src/mbgl/programs/debug_program.hpp +++ b/src/mbgl/programs/debug_program.hpp @@ -4,6 +4,7 @@ #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> #include <mbgl/shaders/debug.hpp> +#include <mbgl/style/properties.hpp> namespace mbgl { @@ -15,7 +16,7 @@ class DebugProgram : public Program< gl::Uniforms< uniforms::u_matrix, uniforms::u_color>, - style::PaintProperties<>> + style::Properties<>> { public: using Program::Program; diff --git a/src/mbgl/programs/extrusion_texture_program.hpp b/src/mbgl/programs/extrusion_texture_program.hpp index 1519aa095d..bd82208885 100644 --- a/src/mbgl/programs/extrusion_texture_program.hpp +++ b/src/mbgl/programs/extrusion_texture_program.hpp @@ -4,6 +4,7 @@ #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> #include <mbgl/shaders/extrusion_texture.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/util/geometry.hpp> namespace mbgl { @@ -17,7 +18,7 @@ class ExtrusionTextureProgram : public Program< uniforms::u_world, uniforms::u_image, uniforms::u_opacity>, - style::PaintProperties<>> { + style::Properties<>> { public: using Program::Program; diff --git a/src/mbgl/programs/fill_extrusion_program.cpp b/src/mbgl/programs/fill_extrusion_program.cpp index 63d1cbeb59..aaf192a843 100644 --- a/src/mbgl/programs/fill_extrusion_program.cpp +++ b/src/mbgl/programs/fill_extrusion_program.cpp @@ -1,5 +1,5 @@ #include <mbgl/programs/fill_extrusion_program.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> +#include <mbgl/renderer/image_atlas.hpp> #include <mbgl/renderer/cross_faded_property_evaluator.hpp> #include <mbgl/tile/tile_id.hpp> #include <mbgl/map/transform_state.hpp> @@ -45,8 +45,9 @@ FillExtrusionUniforms::values(mat4 matrix, FillExtrusionPatternUniforms::Values FillExtrusionPatternUniforms::values(mat4 matrix, - const SpriteAtlasElement& a, - const SpriteAtlasElement& b, + Size atlasSize, + const ImagePosition& a, + const ImagePosition& b, const Faded<std::string>& fading, const UnwrappedTileID& tileID, const TransformState& state, @@ -58,14 +59,15 @@ FillExtrusionPatternUniforms::values(mat4 matrix, return FillExtrusionPatternUniforms::Values{ uniforms::u_matrix::Value{ matrix }, - uniforms::u_pattern_tl_a::Value{ a.tl }, - uniforms::u_pattern_br_a::Value{ a.br }, - uniforms::u_pattern_tl_b::Value{ b.tl }, - uniforms::u_pattern_br_b::Value{ b.br }, - uniforms::u_pattern_size_a::Value{ a.size }, - uniforms::u_pattern_size_b::Value{ b.size }, + uniforms::u_pattern_tl_a::Value{ a.tl() }, + uniforms::u_pattern_br_a::Value{ a.br() }, + uniforms::u_pattern_tl_b::Value{ b.tl() }, + uniforms::u_pattern_br_b::Value{ b.br() }, + uniforms::u_pattern_size_a::Value{ a.displaySize() }, + uniforms::u_pattern_size_b::Value{ b.displaySize() }, uniforms::u_scale_a::Value{ fading.fromScale }, uniforms::u_scale_b::Value{ fading.toScale }, + uniforms::u_texsize::Value{ atlasSize }, uniforms::u_mix::Value{ fading.t }, uniforms::u_image::Value{ 0 }, uniforms::u_pixel_coord_upper::Value{ std::array<float, 2>{{ float(pixelX >> 16), float(pixelY >> 16) }} }, diff --git a/src/mbgl/programs/fill_extrusion_program.hpp b/src/mbgl/programs/fill_extrusion_program.hpp index 48fca44ee8..820670068e 100644 --- a/src/mbgl/programs/fill_extrusion_program.hpp +++ b/src/mbgl/programs/fill_extrusion_program.hpp @@ -16,7 +16,7 @@ namespace mbgl { -class SpriteAtlasElement; +class ImagePosition; class UnwrappedTileID; class TransformState; template <class> class Faded; @@ -55,6 +55,7 @@ struct FillExtrusionPatternUniforms : gl::Uniforms< uniforms::u_pattern_size_b, uniforms::u_scale_a, uniforms::u_scale_b, + uniforms::u_texsize, uniforms::u_mix, uniforms::u_image, uniforms::u_pixel_coord_upper, @@ -66,8 +67,9 @@ struct FillExtrusionPatternUniforms : gl::Uniforms< uniforms::u_lightintensity> { static Values values(mat4, - const SpriteAtlasElement&, - const SpriteAtlasElement&, + Size atlasSize, + const ImagePosition&, + const ImagePosition&, const Faded<std::string>&, const UnwrappedTileID&, const TransformState&, diff --git a/src/mbgl/programs/fill_program.cpp b/src/mbgl/programs/fill_program.cpp index 4310f01164..46dc830102 100644 --- a/src/mbgl/programs/fill_program.cpp +++ b/src/mbgl/programs/fill_program.cpp @@ -1,5 +1,5 @@ #include <mbgl/programs/fill_program.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> +#include <mbgl/renderer/image_atlas.hpp> #include <mbgl/renderer/cross_faded_property_evaluator.hpp> #include <mbgl/tile/tile_id.hpp> #include <mbgl/map/transform_state.hpp> @@ -13,8 +13,9 @@ static_assert(sizeof(FillLayoutVertex) == 4, "expected FillLayoutVertex size"); FillPatternUniforms::Values FillPatternUniforms::values(mat4 matrix, Size framebufferSize, - const SpriteAtlasElement& a, - const SpriteAtlasElement& b, + Size atlasSize, + const ImagePosition& a, + const ImagePosition& b, const Faded<std::string>& fading, const UnwrappedTileID& tileID, const TransformState& state) @@ -26,12 +27,13 @@ FillPatternUniforms::values(mat4 matrix, return FillPatternUniforms::Values { uniforms::u_matrix::Value{ matrix }, uniforms::u_world::Value{ framebufferSize }, - uniforms::u_pattern_tl_a::Value{ a.tl }, - uniforms::u_pattern_br_a::Value{ a.br }, - uniforms::u_pattern_tl_b::Value{ b.tl }, - uniforms::u_pattern_br_b::Value{ b.br }, - uniforms::u_pattern_size_a::Value{ a.size }, - uniforms::u_pattern_size_b::Value{ b.size }, + uniforms::u_texsize::Value{ atlasSize }, + uniforms::u_pattern_tl_a::Value{ a.tl() }, + uniforms::u_pattern_br_a::Value{ a.br() }, + uniforms::u_pattern_tl_b::Value{ b.tl() }, + uniforms::u_pattern_br_b::Value{ b.br() }, + uniforms::u_pattern_size_a::Value{ a.displaySize() }, + uniforms::u_pattern_size_b::Value{ b.displaySize() }, uniforms::u_scale_a::Value{ fading.fromScale }, uniforms::u_scale_b::Value{ fading.toScale }, uniforms::u_mix::Value{ fading.t }, diff --git a/src/mbgl/programs/fill_program.hpp b/src/mbgl/programs/fill_program.hpp index 63751e740a..2dfeea3279 100644 --- a/src/mbgl/programs/fill_program.hpp +++ b/src/mbgl/programs/fill_program.hpp @@ -16,7 +16,7 @@ namespace mbgl { -class SpriteAtlasElement; +class ImagePosition; class UnwrappedTileID; class TransformState; template <class> class Faded; @@ -33,6 +33,7 @@ struct FillUniforms : gl::Uniforms< struct FillPatternUniforms : gl::Uniforms< uniforms::u_matrix, uniforms::u_world, + uniforms::u_texsize, uniforms::u_pattern_tl_a, uniforms::u_pattern_br_a, uniforms::u_pattern_tl_b, @@ -49,8 +50,9 @@ struct FillPatternUniforms : gl::Uniforms< { static Values values(mat4 matrix, Size framebufferSize, - const SpriteAtlasElement&, - const SpriteAtlasElement&, + Size atlasSize, + const ImagePosition&, + const ImagePosition&, const Faded<std::string>&, const UnwrappedTileID&, const TransformState&); diff --git a/src/mbgl/programs/line_program.cpp b/src/mbgl/programs/line_program.cpp index 58e0410b17..faf57ef19b 100644 --- a/src/mbgl/programs/line_program.cpp +++ b/src/mbgl/programs/line_program.cpp @@ -1,9 +1,9 @@ #include <mbgl/programs/line_program.hpp> #include <mbgl/style/layers/line_layer_properties.hpp> #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/image_atlas.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/util/mat2.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/geometry/line_atlas.hpp> namespace mbgl { @@ -13,7 +13,7 @@ using namespace style; static_assert(sizeof(LineLayoutVertex) == 12, "expected LineLayoutVertex size"); template <class Values, class...Args> -Values makeValues(const LinePaintProperties::Evaluated& properties, +Values makeValues(const RenderLinePaintProperties::PossiblyEvaluated& properties, const RenderTile& tile, const TransformState& state, const std::array<float, 2>& pixelsToGLUnits, @@ -25,7 +25,6 @@ Values makeValues(const LinePaintProperties::Evaluated& properties, properties.get<LineTranslateAnchor>(), state) }, - uniforms::u_width::Value{ properties.get<LineWidth>() }, uniforms::u_ratio::Value{ 1.0f / tile.id.pixelsToTileUnits(1.0, state.getZoom()) }, uniforms::u_gl_units_to_pixels::Value{{{ 1.0f / pixelsToGLUnits[0], 1.0f / pixelsToGLUnits[1] }}}, std::forward<Args>(args)... @@ -33,7 +32,7 @@ Values makeValues(const LinePaintProperties::Evaluated& properties, } LineProgram::UniformValues -LineProgram::uniformValues(const LinePaintProperties::Evaluated& properties, +LineProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& properties, const RenderTile& tile, const TransformState& state, const std::array<float, 2>& pixelsToGLUnits) { @@ -46,17 +45,16 @@ LineProgram::uniformValues(const LinePaintProperties::Evaluated& properties, } LineSDFProgram::UniformValues -LineSDFProgram::uniformValues(const LinePaintProperties::Evaluated& properties, +LineSDFProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& properties, float pixelRatio, const RenderTile& tile, const TransformState& state, const std::array<float, 2>& pixelsToGLUnits, const LinePatternPos& posA, const LinePatternPos& posB, - float dashLineWidth, float atlasWidth) { - const float widthA = posA.width * properties.get<LineDasharray>().fromScale * dashLineWidth; - const float widthB = posB.width * properties.get<LineDasharray>().toScale * dashLineWidth; + const float widthA = posA.width * properties.get<LineDasharray>().fromScale; + const float widthB = posB.width * properties.get<LineDasharray>().toScale; std::array<float, 2> scaleA {{ 1.0f / tile.id.pixelsToTileUnits(widthA, state.getIntegerZoom()), @@ -84,20 +82,21 @@ LineSDFProgram::uniformValues(const LinePaintProperties::Evaluated& properties, } LinePatternProgram::UniformValues -LinePatternProgram::uniformValues(const LinePaintProperties::Evaluated& properties, +LinePatternProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& properties, const RenderTile& tile, const TransformState& state, const std::array<float, 2>& pixelsToGLUnits, - const SpriteAtlasElement& posA, - const SpriteAtlasElement& posB) { + const Size atlasSize, + const ImagePosition& posA, + const ImagePosition& posB) { std::array<float, 2> sizeA {{ - tile.id.pixelsToTileUnits(posA.size[0] * properties.get<LinePattern>().fromScale, state.getIntegerZoom()), - posA.size[1] + tile.id.pixelsToTileUnits(posA.displaySize()[0] * properties.get<LinePattern>().fromScale, state.getIntegerZoom()), + posA.displaySize()[1] }}; std::array<float, 2> sizeB {{ - tile.id.pixelsToTileUnits(posB.size[0] * properties.get<LinePattern>().toScale, state.getIntegerZoom()), - posB.size[1] + tile.id.pixelsToTileUnits(posB.displaySize()[0] * properties.get<LinePattern>().toScale, state.getIntegerZoom()), + posB.displaySize()[1] }}; return makeValues<LinePatternProgram::UniformValues>( @@ -105,12 +104,13 @@ LinePatternProgram::uniformValues(const LinePaintProperties::Evaluated& properti tile, state, pixelsToGLUnits, - uniforms::u_pattern_tl_a::Value{ posA.tl }, - uniforms::u_pattern_br_a::Value{ posA.br }, - uniforms::u_pattern_tl_b::Value{ posB.tl }, - uniforms::u_pattern_br_b::Value{ posB.br }, + uniforms::u_pattern_tl_a::Value{ posA.tl() }, + uniforms::u_pattern_br_a::Value{ posA.br() }, + uniforms::u_pattern_tl_b::Value{ posB.tl() }, + uniforms::u_pattern_br_b::Value{ posB.br() }, uniforms::u_pattern_size_a::Value{ sizeA }, uniforms::u_pattern_size_b::Value{ sizeB }, + uniforms::u_texsize::Value{ atlasSize }, uniforms::u_fade::Value{ properties.get<LinePattern>().t }, uniforms::u_image::Value{ 0 } ); diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp index 9cfc2d3257..da9964e623 100644 --- a/src/mbgl/programs/line_program.hpp +++ b/src/mbgl/programs/line_program.hpp @@ -7,7 +7,7 @@ #include <mbgl/shaders/line_pattern.hpp> #include <mbgl/shaders/line_sdf.hpp> #include <mbgl/util/geometry.hpp> -#include <mbgl/style/layers/line_layer_properties.hpp> +#include <mbgl/renderer/layers/render_line_layer.hpp> #include <cmath> @@ -16,11 +16,10 @@ namespace mbgl { class RenderTile; class TransformState; class LinePatternPos; -class SpriteAtlasElement; +class ImagePosition; namespace uniforms { MBGL_DEFINE_UNIFORM_SCALAR(float, u_ratio); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_width); MBGL_DEFINE_UNIFORM_SCALAR(float, u_tex_y_a); MBGL_DEFINE_UNIFORM_SCALAR(float, u_tex_y_b); MBGL_DEFINE_UNIFORM_SCALAR(float, u_sdfgamma); @@ -41,10 +40,9 @@ class LineProgram : public Program< LineLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_width, uniforms::u_ratio, uniforms::u_gl_units_to_pixels>, - style::LinePaintProperties> + RenderLinePaintProperties> { public: using Program::Program; @@ -94,7 +92,7 @@ public: */ static const int8_t extrudeScale = 63; - static UniformValues uniformValues(const style::LinePaintProperties::Evaluated&, + static UniformValues uniformValues(const RenderLinePaintProperties::PossiblyEvaluated&, const RenderTile&, const TransformState&, const std::array<float, 2>& pixelsToGLUnits); @@ -106,7 +104,6 @@ class LinePatternProgram : public Program< LineLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_width, uniforms::u_ratio, uniforms::u_gl_units_to_pixels, uniforms::u_pattern_tl_a, @@ -115,19 +112,21 @@ class LinePatternProgram : public Program< uniforms::u_pattern_br_b, uniforms::u_pattern_size_a, uniforms::u_pattern_size_b, + uniforms::u_texsize, uniforms::u_fade, uniforms::u_image>, - style::LinePaintProperties> + RenderLinePaintProperties> { public: using Program::Program; - static UniformValues uniformValues(const style::LinePaintProperties::Evaluated&, + static UniformValues uniformValues(const RenderLinePaintProperties::PossiblyEvaluated&, const RenderTile&, const TransformState&, const std::array<float, 2>& pixelsToGLUnits, - const SpriteAtlasElement& posA, - const SpriteAtlasElement& posB); + Size atlasSize, + const ImagePosition& posA, + const ImagePosition& posB); }; class LineSDFProgram : public Program< @@ -136,7 +135,6 @@ class LineSDFProgram : public Program< LineLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_width, uniforms::u_ratio, uniforms::u_gl_units_to_pixels, uniforms::u_patternscale_a, @@ -146,19 +144,18 @@ class LineSDFProgram : public Program< uniforms::u_mix, uniforms::u_sdfgamma, uniforms::u_image>, - style::LinePaintProperties> + RenderLinePaintProperties> { public: using Program::Program; - static UniformValues uniformValues(const style::LinePaintProperties::Evaluated&, + static UniformValues uniformValues(const RenderLinePaintProperties::PossiblyEvaluated&, float pixelRatio, const RenderTile&, const TransformState&, const std::array<float, 2>& pixelsToGLUnits, const LinePatternPos& posA, const LinePatternPos& posB, - float dashLineWidth, float atlasWidth); }; diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp index ca8434cf0a..bcdb270b9c 100644 --- a/src/mbgl/programs/program.hpp +++ b/src/mbgl/programs/program.hpp @@ -57,7 +57,7 @@ public: const gl::IndexBuffer<DrawMode>& indexBuffer, const SegmentVector<Attributes>& segments, const PaintPropertyBinders& paintPropertyBinders, - const typename PaintProperties::Evaluated& currentProperties, + const typename PaintProperties::PossiblyEvaluated& currentProperties, float currentZoom, const std::string& layerID) { typename AllUniforms::Values allUniformValues = uniformValues @@ -101,7 +101,7 @@ public: parameters(std::move(parameters_)) { } - Program& get(const typename PaintProperties::Evaluated& currentProperties) { + Program& get(const typename PaintProperties::PossiblyEvaluated& currentProperties) { Bitset bits = PaintPropertyBinders::constants(currentProperties); auto it = programs.find(bits); if (it != programs.end()) { diff --git a/src/mbgl/programs/segment.cpp b/src/mbgl/programs/segment.cpp deleted file mode 100644 index bb09843e21..0000000000 --- a/src/mbgl/programs/segment.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include <mbgl/programs/segment.hpp> - -namespace mbgl { -namespace gl { - -} // namespace gl -} // namespace mbgl diff --git a/src/mbgl/programs/segment.hpp b/src/mbgl/programs/segment.hpp index 937df4dece..f729683ac9 100644 --- a/src/mbgl/programs/segment.hpp +++ b/src/mbgl/programs/segment.hpp @@ -38,9 +38,6 @@ public: }; template <class Attributes> -class SegmentVector : public std::vector<Segment<Attributes>> { -public: - SegmentVector() = default; -}; +using SegmentVector = std::vector<Segment<Attributes>>; } // namespace mbgl diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 86f61c4ad2..58174ff8a7 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -2,6 +2,8 @@ #include <mbgl/renderer/render_tile.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/layout/symbol_projection.hpp> +#include <mbgl/tile/tile.hpp> #include <mbgl/util/enum.hpp> #include <mbgl/math/clamp.hpp> @@ -32,6 +34,7 @@ Values makeValues(const bool isText, const style::SymbolPropertyValues& values, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, + const bool alongLine, const RenderTile& tile, const TransformState& state, Args&&... args) { @@ -45,18 +48,49 @@ Values makeValues(const bool isText, pixelsToGLUnits[1] * state.getCameraToCenterDistance() }}; } + + const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom()); + const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map; + const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map; + + // Line label rotation happens in `updateLineLabels` + // Pitched point labels are automatically rotated by the labelPlaneMatrix projection + // Unpitched point labels need to have their rotation applied after projection + const bool rotateInShader = rotateWithMap && !pitchWithMap && !alongLine; + + mat4 labelPlaneMatrix; + if (alongLine) { + // For labels that follow lines the first part of the projection is handled on the cpu. + // Pass an identity matrix because no transformation needs to be done in the vertex shader. + matrix::identity(labelPlaneMatrix); + } else { + labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits); + } + + mat4 glCoordMatrix = getGlCoordMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits); return Values { uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate, values.translateAnchor, state) }, + uniforms::u_label_plane_matrix::Value{labelPlaneMatrix}, + uniforms::u_gl_coord_matrix::Value{ tile.translateVtxMatrix(glCoordMatrix, + values.translate, + values.translateAnchor, + state, + true) }, uniforms::u_extrude_scale::Value{ extrudeScale }, - uniforms::u_texsize::Value{ std::array<float, 2> {{ float(texsize.width) / 4, float(texsize.height) / 4 }} }, - uniforms::u_zoom::Value{ float(state.getZoom()) }, - uniforms::u_rotate_with_map::Value{ values.rotationAlignment == AlignmentType::Map }, + uniforms::u_texsize::Value{ texsize }, uniforms::u_texture::Value{ 0 }, uniforms::u_fadetexture::Value{ 1 }, uniforms::u_is_text::Value{ isText }, + uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, + uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() }, + uniforms::u_pitch::Value{ state.getPitch() }, + uniforms::u_pitch_with_map::Value{ pitchWithMap }, + uniforms::u_max_camera_distance::Value{ values.maxCameraDistance }, + uniforms::u_rotate_symbol::Value{ rotateInShader }, + uniforms::u_aspect_ratio::Value{ state.getSize().aspectRatio() }, std::forward<Args>(args)... }; } @@ -66,6 +100,7 @@ SymbolIconProgram::uniformValues(const bool isText, const style::SymbolPropertyValues& values, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, + const bool alongLine, const RenderTile& tile, const TransformState& state) { @@ -74,6 +109,7 @@ SymbolIconProgram::uniformValues(const bool isText, values, texsize, pixelsToGLUnits, + alongLine, tile, state ); @@ -85,26 +121,24 @@ typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<Paint const style::SymbolPropertyValues& values, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, + const bool alongLine, const RenderTile& tile, const TransformState& state, const SymbolSDFPart part) { const float gammaScale = (values.pitchAlignment == AlignmentType::Map - ? std::cos(state.getPitch()) - : 1.0) * state.getCameraToCenterDistance(); + ? std::cos(state.getPitch()) * state.getCameraToCenterDistance() + : 1.0); return makeValues<SymbolSDFProgram<PaintProperties>::UniformValues>( isText, values, texsize, pixelsToGLUnits, + alongLine, tile, state, uniforms::u_gamma_scale::Value{ gammaScale }, - uniforms::u_pitch::Value{ state.getPitch() }, - uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, - uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, - uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map }, uniforms::u_is_halo::Value{ part == SymbolSDFPart::Halo } ); } diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 5fb0c4f15f..a7abf94f56 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -14,7 +14,7 @@ #include <mbgl/util/size.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <cmath> @@ -30,12 +30,9 @@ class RenderTile; class TransformState; namespace uniforms { -MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_texsize); -MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_with_map); -MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map); +MBGL_DEFINE_UNIFORM_MATRIX(double, 4, u_gl_coord_matrix); +MBGL_DEFINE_UNIFORM_MATRIX(double, 4, u_label_plane_matrix); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture); -MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo); MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale); @@ -44,50 +41,60 @@ MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_zoom_constant); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_feature_constant); MBGL_DEFINE_UNIFORM_SCALAR(float, u_size_t); MBGL_DEFINE_UNIFORM_SCALAR(float, u_size); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_layout_size); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_max_camera_distance); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_symbol); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); } // namespace uniforms struct SymbolLayoutAttributes : gl::Attributes< attributes::a_pos_offset, attributes::a_data<uint16_t, 4>> { - static Vertex vertex(Point<float> a, + static Vertex vertex(Point<float> labelAnchor, Point<float> o, + float glyphOffsetY, uint16_t tx, uint16_t ty, - float minzoom, - float maxzoom, - float labelminzoom, - uint8_t labelangle) { + const Range<float>& sizeData) { return Vertex { // combining pos and offset to reduce number of vertex attributes passed to shader (8 max for some devices) {{ - static_cast<int16_t>(a.x), - static_cast<int16_t>(a.y), + static_cast<int16_t>(labelAnchor.x), + static_cast<int16_t>(labelAnchor.y), static_cast<int16_t>(::round(o.x * 64)), // use 1/64 pixels for placement - static_cast<int16_t>(::round(o.y * 64)) + static_cast<int16_t>(::round((o.y + glyphOffsetY) * 64)) }}, {{ - static_cast<uint16_t>(tx / 4), - static_cast<uint16_t>(ty / 4), - mbgl::attributes::packUint8Pair( - static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160 - static_cast<uint8_t>(labelangle) - ), - mbgl::attributes::packUint8Pair( - static_cast<uint8_t>(minzoom * 10), - static_cast<uint8_t>(::fmin(maxzoom, 25) * 10) - ) + tx, + ty, + static_cast<uint16_t>(sizeData.min * 10), + static_cast<uint16_t>(sizeData.max * 10) }} }; } }; + +struct SymbolDynamicLayoutAttributes : gl::Attributes<attributes::a_projected_pos> { + static Vertex vertex(Point<float> anchorPoint, float labelAngle, float labelminzoom) { + return Vertex { + {{ + anchorPoint.x, + anchorPoint.y, + static_cast<float>(mbgl::attributes::packUint8Pair( + static_cast<uint8_t>(std::fmod(labelAngle + 2 * M_PI, 2 * M_PI) / (2 * M_PI) * 255), + static_cast<uint8_t>(labelminzoom * 10))) + }} + }; + } +}; -class SymbolSizeAttributes : public gl::Attributes<attributes::a_size> { -public: - using Attribute = attributes::a_size::Type; +struct ZoomEvaluatedSize { + bool isZoomConstant; + bool isFeatureConstant; + float sizeT; + float size; + float layoutSize; }; - // Mimic the PaintPropertyBinder technique specifically for the {text,icon}-size layout properties // in order to provide a 'custom' scheme for encoding the necessary attribute data. As with // PaintPropertyBinder, SymbolSizeBinder is an abstract class whose implementations handle the @@ -100,18 +107,25 @@ public: uniforms::u_is_size_zoom_constant, uniforms::u_is_size_feature_constant, uniforms::u_size_t, - uniforms::u_size, - uniforms::u_layout_size>; + uniforms::u_size>; using UniformValues = Uniforms::Values; static std::unique_ptr<SymbolSizeBinder> create(const float tileZoom, const style::DataDrivenPropertyValue<float>& sizeProperty, const float defaultValue); - virtual SymbolSizeAttributes::Bindings attributeBindings() const = 0; - virtual void populateVertexVector(const GeometryTileFeature& feature) = 0; - virtual UniformValues uniformValues(float currentZoom) const = 0; - virtual void upload(gl::Context&) = 0; + virtual Range<float> getVertexSizeData(const GeometryTileFeature& feature) = 0; + virtual ZoomEvaluatedSize evaluateForZoom(float currentZoom) const = 0; + + UniformValues uniformValues(float currentZoom) const { + const ZoomEvaluatedSize u = evaluateForZoom(currentZoom); + return UniformValues { + uniforms::u_is_size_zoom_constant::Value{ u.isZoomConstant }, + uniforms::u_is_size_feature_constant::Value{ u.isFeatureConstant}, + uniforms::u_size_t::Value{ u.sizeT }, + uniforms::u_size::Value{ u.size } + }; + } }; // Return the smallest range of stops that covers the interval [lowerZoom, upperZoom] @@ -123,7 +137,7 @@ Range<float> getCoveringStops(Stops s, float lowerZoom, float upperZoom) { // lower_bound yields first element >= lowerZoom, but we want the *last* // element <= lowerZoom, so if we found a stop > lowerZoom, back up by one. - if (minIt != s.stops.begin() && minIt->first > lowerZoom) { + if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > lowerZoom) { minIt--; } return Range<float> { @@ -144,9 +158,10 @@ public: : layoutSize(function_.evaluate(tileZoom + 1)) { function_.stops.match( [&] (const style::ExponentialStops<float>& stops) { + const auto& zoomLevels = getCoveringStops(stops, tileZoom, tileZoom + 1); coveringRanges = std::make_tuple( - getCoveringStops(stops, tileZoom, tileZoom + 1), - Range<float> { function_.evaluate(tileZoom), function_.evaluate(tileZoom + 1) } + zoomLevels, + Range<float> { function_.evaluate(zoomLevels.min), function_.evaluate(zoomLevels.max) } ); functionInterpolationBase = stops.base; }, @@ -156,14 +171,9 @@ public: ); } - SymbolSizeAttributes::Bindings attributeBindings() const override { - return SymbolSizeAttributes::Bindings { {} }; - } - - void upload(gl::Context&) override {} - void populateVertexVector(const GeometryTileFeature&) override {}; + Range<float> getVertexSizeData(const GeometryTileFeature&) override { return { 0.0f, 0.0f }; }; - UniformValues uniformValues(float currentZoom) const override { + ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override { float size = layoutSize; bool isZoomConstant = !(coveringRanges || function); if (coveringRanges) { @@ -182,14 +192,9 @@ public: } else if (function) { size = function->evaluate(currentZoom); } - - return UniformValues { - uniforms::u_is_size_zoom_constant::Value{ isZoomConstant }, - uniforms::u_is_size_feature_constant::Value{ true }, - uniforms::u_size_t::Value{ 0.0f }, // unused - uniforms::u_size::Value{ size }, - uniforms::u_layout_size::Value{ layoutSize } - }; + + const float unused = 0.0f; + return { isZoomConstant, true, unused, size, layoutSize }; } float layoutSize; @@ -211,49 +216,22 @@ public: defaultValue(defaultValue_) { } - SymbolSizeAttributes::Bindings attributeBindings() const override { - return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::binding(*buffer, 0, 1) }; - } - - void populateVertexVector(const GeometryTileFeature& feature) override { - const auto sizeVertex = Vertex { - {{ - static_cast<uint16_t>(function.evaluate(feature, defaultValue) * 10) - }} - }; - - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); + Range<float> getVertexSizeData(const GeometryTileFeature& feature) override { + const float size = function.evaluate(feature, defaultValue); + return { size, size }; }; - UniformValues uniformValues(float) const override { - return UniformValues { - uniforms::u_is_size_zoom_constant::Value{ true }, - uniforms::u_is_size_feature_constant::Value{ false }, - uniforms::u_size_t::Value{ 0.0f }, // unused - uniforms::u_size::Value{ 0.0f }, // unused - uniforms::u_layout_size::Value{ 0.0f } // unused - }; - } - - void upload(gl::Context& context) override { - buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) }; + ZoomEvaluatedSize evaluateForZoom(float) const override { + const float unused = 0.0f; + return { true, false, unused, unused, unused }; } const style::SourceFunction<float>& function; const float defaultValue; - - VertexVector vertices; - optional<VertexBuffer> buffer; }; class CompositeFunctionSymbolSizeBinder final : public SymbolSizeBinder { public: - using Vertex = SymbolSizeAttributes::Vertex; - using VertexVector = gl::VertexVector<Vertex>; - using VertexBuffer = gl::VertexBuffer<Vertex>; CompositeFunctionSymbolSizeBinder(const float tileZoom, const style::CompositeFunction<float>& function_, const float defaultValue_) : function(function_), @@ -264,51 +242,27 @@ public: return getCoveringStops(stops, tileZoom, tileZoom + 1); })) {} - SymbolSizeAttributes::Bindings attributeBindings() const override { - return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::binding(*buffer, 0) }; - } - - void populateVertexVector(const GeometryTileFeature& feature) override { - const auto sizeVertex = Vertex { - {{ - static_cast<uint16_t>(function.evaluate(coveringZoomStops.min, feature, defaultValue) * 10), - static_cast<uint16_t>(function.evaluate(coveringZoomStops.max, feature, defaultValue) * 10), - static_cast<uint16_t>(function.evaluate(layoutZoom, feature, defaultValue) * 10) - }} + Range<float> getVertexSizeData(const GeometryTileFeature& feature) override { + return { + function.evaluate(coveringZoomStops.min, feature, defaultValue), + function.evaluate(coveringZoomStops.max, feature, defaultValue) }; - - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); - vertices.emplace_back(sizeVertex); }; - UniformValues uniformValues(float currentZoom) const override { + ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override { float sizeInterpolationT = util::clamp( util::interpolationFactor(1.0f, coveringZoomStops, currentZoom), 0.0f, 1.0f ); - return UniformValues { - uniforms::u_is_size_zoom_constant::Value{ false }, - uniforms::u_is_size_feature_constant::Value{ false }, - uniforms::u_size_t::Value{ sizeInterpolationT }, - uniforms::u_size::Value{ 0.0f }, // unused - uniforms::u_layout_size::Value{ 0.0f } // unused - }; - } - - void upload(gl::Context& context) override { - buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) }; + const float unused = 0.0f; + return { false, false, sizeInterpolationT, unused, unused }; } const style::CompositeFunction<float>& function; const float defaultValue; float layoutZoom; Range<float> coveringZoomStops; - - VertexVector vertices; - optional<VertexBuffer> buffer; }; @@ -322,7 +276,7 @@ public: using LayoutAttributes = LayoutAttrs; using LayoutVertex = typename LayoutAttributes::Vertex; - using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolSizeAttributes>; + using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolDynamicLayoutAttributes>; using PaintProperties = PaintProps; using PaintPropertyBinders = typename PaintProperties::Binders; @@ -355,11 +309,12 @@ public: gl::ColorMode colorMode, const UniformValues& uniformValues, const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer, + const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>& dynamicLayoutVertexBuffer, const SymbolSizeBinder& symbolSizeBinder, const gl::IndexBuffer<DrawMode>& indexBuffer, const SegmentVector<Attributes>& segments, const PaintPropertyBinders& paintPropertyBinders, - const typename PaintProperties::Evaluated& currentProperties, + const typename PaintProperties::PossiblyEvaluated& currentProperties, float currentZoom, const std::string& layerID) { typename AllUniforms::Values allUniformValues = uniformValues @@ -367,7 +322,7 @@ public: .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer) - .concat(symbolSizeBinder.attributeBindings()) + .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer)) .concat(paintPropertyBinders.attributeBindings(currentProperties)); for (auto& segment : segments) { @@ -399,13 +354,20 @@ class SymbolIconProgram : public SymbolProgram< SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, + uniforms::u_label_plane_matrix, + uniforms::u_gl_coord_matrix, uniforms::u_extrude_scale, uniforms::u_texsize, - uniforms::u_zoom, - uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, - uniforms::u_is_text>, + uniforms::u_is_text, + uniforms::u_collision_y_stretch, + uniforms::u_camera_to_center_distance, + uniforms::u_pitch, + uniforms::u_pitch_with_map, + uniforms::u_max_camera_distance, + uniforms::u_rotate_symbol, + uniforms::u_aspect_ratio>, style::IconPaintProperties> { public: @@ -415,6 +377,7 @@ public: const style::SymbolPropertyValues&, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, + const bool alongLine, const RenderTile&, const TransformState&); }; @@ -431,18 +394,21 @@ class SymbolSDFProgram : public SymbolProgram< SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, + uniforms::u_label_plane_matrix, + uniforms::u_gl_coord_matrix, uniforms::u_extrude_scale, uniforms::u_texsize, - uniforms::u_zoom, - uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, uniforms::u_is_text, - uniforms::u_gamma_scale, + uniforms::u_collision_y_stretch, + uniforms::u_camera_to_center_distance, uniforms::u_pitch, - uniforms::u_bearing, - uniforms::u_aspect_ratio, uniforms::u_pitch_with_map, + uniforms::u_max_camera_distance, + uniforms::u_rotate_symbol, + uniforms::u_aspect_ratio, + uniforms::u_gamma_scale, uniforms::u_is_halo>, PaintProperties> { @@ -452,18 +418,21 @@ public: SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, + uniforms::u_label_plane_matrix, + uniforms::u_gl_coord_matrix, uniforms::u_extrude_scale, uniforms::u_texsize, - uniforms::u_zoom, - uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, uniforms::u_is_text, - uniforms::u_gamma_scale, + uniforms::u_collision_y_stretch, + uniforms::u_camera_to_center_distance, uniforms::u_pitch, - uniforms::u_bearing, + uniforms::u_pitch_with_map, + uniforms::u_max_camera_distance, + uniforms::u_rotate_symbol, uniforms::u_aspect_ratio, - uniforms::u_pitch_with_map, + uniforms::u_gamma_scale, uniforms::u_is_halo>, PaintProperties>; @@ -477,6 +446,7 @@ public: const style::SymbolPropertyValues&, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, + const bool alongLine, const RenderTile&, const TransformState&, const SymbolSDFPart); diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp index 32d857a94e..285d243251 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -14,6 +14,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(Color, u_color); MBGL_DEFINE_UNIFORM_SCALAR(float, u_blur); MBGL_DEFINE_UNIFORM_SCALAR(float, u_zoom); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_collision_y_stretch); MBGL_DEFINE_UNIFORM_SCALAR(float, u_pitch); MBGL_DEFINE_UNIFORM_SCALAR(float, u_bearing); MBGL_DEFINE_UNIFORM_SCALAR(float, u_radius); @@ -27,16 +28,21 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_halo_blur); MBGL_DEFINE_UNIFORM_SCALAR(Color, u_outline_color); MBGL_DEFINE_UNIFORM_SCALAR(float, u_height); MBGL_DEFINE_UNIFORM_SCALAR(float, u_base); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_width); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_floorwidth); MBGL_DEFINE_UNIFORM_SCALAR(float, u_gapwidth); MBGL_DEFINE_UNIFORM_SCALAR(float, u_offset); MBGL_DEFINE_UNIFORM_SCALAR(Size, u_world); +MBGL_DEFINE_UNIFORM_SCALAR(Size, u_texsize); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_camera_to_center_distance); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_extrude_scale); -MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_tl_a); -MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_br_a); -MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_tl_b); -MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_br_b); +MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_a); +MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_br_a); +MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_b); +MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_br_b); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_size_a); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_size_b); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_upper); @@ -44,6 +50,7 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_lower); MBGL_DEFINE_UNIFORM_SCALAR(float, u_mix); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_image); +MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture); MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a); MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b); MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels); diff --git a/src/mbgl/map/backend_scope.cpp b/src/mbgl/renderer/backend_scope.cpp index dac90346d7..fafeaabb39 100644 --- a/src/mbgl/map/backend_scope.cpp +++ b/src/mbgl/renderer/backend_scope.cpp @@ -1,5 +1,5 @@ -#include <mbgl/map/backend_scope.hpp> -#include <mbgl/map/backend.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/renderer/renderer_backend.hpp> #include <mbgl/util/thread_local.hpp> #include <cassert> @@ -8,7 +8,7 @@ namespace mbgl { static util::ThreadLocal<BackendScope> currentScope; -BackendScope::BackendScope(Backend& backend_, ScopeType scopeType_) +BackendScope::BackendScope(RendererBackend& backend_, ScopeType scopeType_) : priorScope(currentScope.get()), nextScope(nullptr), backend(backend_), diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp index 6c391cf014..9af511a03e 100644 --- a/src/mbgl/renderer/bucket.hpp +++ b/src/mbgl/renderer/bucket.hpp @@ -1,33 +1,26 @@ #pragma once -#include <mbgl/renderer/render_pass.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/tile/geometry_tile_data.hpp> -#include <mbgl/renderer/render_layer.hpp> #include <atomic> -#include <string> -#include <unordered_map> namespace mbgl { -class Painter; -class PaintParameters; -class RenderTile; - namespace gl { class Context; } // namespace gl -namespace style { -class Layer; -} // namespace style +class RenderLayer; class Bucket : private util::noncopyable { public: Bucket() = default; virtual ~Bucket() = default; + // Feature geometries are also used to populate the feature index. + // Obtaining these is a costly operation, so we do it only once, and + // pass-by-const-ref the geometries as a second parameter. virtual void addFeature(const GeometryTileFeature&, const GeometryCollection&) {}; @@ -35,10 +28,6 @@ public: // this only happens once when the bucket is being rendered for the first time. virtual void upload(gl::Context&) = 0; - // Every time this bucket is getting rendered, this function is called. This happens either - // once or twice (for Opaque and Transparent render passes). - virtual void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) = 0; - virtual bool hasData() const = 0; virtual float getQueryRadius(const RenderLayer&) const { @@ -46,7 +35,7 @@ public: }; bool needsUpload() const { - return !uploaded; + return hasData() && !uploaded; } protected: diff --git a/src/mbgl/renderer/bucket_parameters.hpp b/src/mbgl/renderer/bucket_parameters.hpp index 1774ba2bbe..50ec4cf521 100644 --- a/src/mbgl/renderer/bucket_parameters.hpp +++ b/src/mbgl/renderer/bucket_parameters.hpp @@ -9,6 +9,7 @@ class BucketParameters { public: const OverscaledTileID tileID; const MapMode mode; + const float pixelRatio; }; } // namespace mbgl diff --git a/src/mbgl/renderer/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp index 1e08eca478..04126990b3 100644 --- a/src/mbgl/renderer/circle_bucket.cpp +++ b/src/mbgl/renderer/buckets/circle_bucket.cpp @@ -1,9 +1,8 @@ -#include <mbgl/renderer/circle_bucket.hpp> +#include <mbgl/renderer/buckets/circle_bucket.hpp> #include <mbgl/renderer/bucket_parameters.hpp> -#include <mbgl/renderer/painter.hpp> #include <mbgl/programs/circle_program.hpp> #include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/renderer/render_circle_layer.hpp> +#include <mbgl/renderer/layers/render_circle_layer.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/math.hpp> @@ -34,13 +33,6 @@ void CircleBucket::upload(gl::Context& context) { uploaded = true; } -void CircleBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderCircle(parameters, *this, *layer.as<RenderCircleLayer>(), tile); -} - bool CircleBucket::hasData() const { return !segments.empty(); } diff --git a/src/mbgl/renderer/circle_bucket.hpp b/src/mbgl/renderer/buckets/circle_bucket.hpp index 0f27e2a7e3..78b6351bcb 100644 --- a/src/mbgl/renderer/circle_bucket.hpp +++ b/src/mbgl/renderer/buckets/circle_bucket.hpp @@ -23,8 +23,6 @@ public: void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; - float getQueryRadius(const RenderLayer&) const override; gl::VertexVector<CircleLayoutVertex> vertices; diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/buckets/debug_bucket.cpp index 2a514989cf..53c751c443 100644 --- a/src/mbgl/renderer/debug_bucket.cpp +++ b/src/mbgl/renderer/buckets/debug_bucket.cpp @@ -1,7 +1,7 @@ -#include <mbgl/renderer/debug_bucket.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/buckets/debug_bucket.hpp> #include <mbgl/programs/fill_program.hpp> #include <mbgl/geometry/debug_font_data.hpp> +#include <mbgl/tile/tile_id.hpp> #include <mbgl/util/string.hpp> #include <cmath> diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/buckets/debug_bucket.hpp index fc3128e944..fc3128e944 100644 --- a/src/mbgl/renderer/debug_bucket.hpp +++ b/src/mbgl/renderer/buckets/debug_bucket.hpp diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp index 2409fd365b..110db887a1 100644 --- a/src/mbgl/renderer/fill_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_bucket.cpp @@ -1,9 +1,8 @@ -#include <mbgl/renderer/fill_bucket.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/buckets/fill_bucket.hpp> #include <mbgl/programs/fill_program.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/renderer/render_fill_layer.hpp> +#include <mbgl/renderer/layers/render_fill_layer.hpp> #include <mbgl/util/math.hpp> #include <mapbox/earcut.hpp> @@ -121,13 +120,6 @@ void FillBucket::upload(gl::Context& context) { uploaded = true; } -void FillBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderFill(parameters, *this, *layer.as<RenderFillLayer>(), tile); -} - bool FillBucket::hasData() const { return !triangleSegments.empty() || !lineSegments.empty(); } diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp index d3cd92d451..a50e1971f5 100644 --- a/src/mbgl/renderer/fill_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_bucket.hpp @@ -23,7 +23,6 @@ public: bool hasData() const override; void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; float getQueryRadius(const RenderLayer&) const override; diff --git a/src/mbgl/renderer/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp index 2b352ab66a..7f53326fe1 100644 --- a/src/mbgl/renderer/fill_extrusion_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -1,9 +1,8 @@ -#include <mbgl/renderer/fill_extrusion_bucket.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp> #include <mbgl/programs/fill_extrusion_program.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/constants.hpp> @@ -154,13 +153,6 @@ void FillExtrusionBucket::upload(gl::Context& context) { uploaded = true; } -void FillExtrusionBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderFillExtrusion(parameters, *this, *layer.as<RenderFillExtrusionLayer>(), tile); -} - bool FillExtrusionBucket::hasData() const { return !triangleSegments.empty(); } diff --git a/src/mbgl/renderer/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp index d1e695c5a3..d57265ab16 100644 --- a/src/mbgl/renderer/fill_extrusion_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp @@ -21,7 +21,6 @@ public: bool hasData() const override; void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; float getQueryRadius(const RenderLayer&) const override; diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp index c80b8900ea..a96518df38 100644 --- a/src/mbgl/renderer/line_bucket.cpp +++ b/src/mbgl/renderer/buckets/line_bucket.cpp @@ -1,6 +1,5 @@ -#include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/render_line_layer.hpp> +#include <mbgl/renderer/buckets/line_bucket.hpp> +#include <mbgl/renderer/layers/render_line_layer.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/util/math.hpp> @@ -14,9 +13,10 @@ using namespace style; LineBucket::LineBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers, - const style::LineLayoutProperties& layout_) + const style::LineLayoutProperties::Unevaluated& layout_) : layout(layout_.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ))), - overscaling(parameters.tileID.overscaleFactor()) { + overscaling(parameters.tileID.overscaleFactor()), + zoom(parameters.tileID.overscaledZ) { for (const auto& layer : layers) { paintPropertyBinders.emplace( std::piecewise_construct, @@ -30,7 +30,7 @@ LineBucket::LineBucket(const BucketParameters& parameters, void LineBucket::addFeature(const GeometryTileFeature& feature, const GeometryCollection& geometryCollection) { for (auto& line : geometryCollection) { - addGeometry(line, feature.getType()); + addGeometry(line, feature); } for (auto& pair : paintPropertyBinders) { @@ -63,7 +63,8 @@ const float LINE_DISTANCE_SCALE = 1.0 / 2.0; // The maximum line distance, in tile units, that fits in the buffer. const float MAX_LINE_DISTANCE = std::pow(2, LINE_DISTANCE_BUFFER_BITS) / LINE_DISTANCE_SCALE; -void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType type) { +void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const GeometryTileFeature& feature) { + const FeatureType type = feature.getType(); const std::size_t len = [&coordinates] { std::size_t l = coordinates.size(); // If the line has duplicate vertices at the end, adjust length to remove them. @@ -87,7 +88,9 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType return; } - const float miterLimit = layout.get<LineJoin>() == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>()); + const LineJoinType joinType = layout.evaluate<LineJoin>(zoom, feature); + + const float miterLimit = joinType == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>()); const double sharpCornerOffset = SHARP_CORNER_OFFSET * (float(util::EXTENT) / (util::tileSize * overscaling)); @@ -183,7 +186,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType const bool isSharpCorner = cosHalfAngle < COS_HALF_SHARP_CORNER && prevCoordinate && nextCoordinate; if (isSharpCorner && i > first) { - const double prevSegmentLength = util::dist<double>(*currentCoordinate, *prevCoordinate); + const auto prevSegmentLength = util::dist<double>(*currentCoordinate, *prevCoordinate); if (prevSegmentLength > 2.0 * sharpCornerOffset) { GeometryCoordinate newPrevVertex = *currentCoordinate - convertPoint<int16_t>(util::round(convertPoint<double>(*currentCoordinate - *prevCoordinate) * (sharpCornerOffset / prevSegmentLength))); distance += util::dist<double>(newPrevVertex, *prevCoordinate); @@ -194,7 +197,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType // The join if a middle vertex, otherwise the cap const bool middleVertex = prevCoordinate && nextCoordinate; - LineJoinType currentJoin = layout.get<LineJoin>(); + LineJoinType currentJoin = joinType; const LineCapType currentCap = nextCoordinate ? beginCap : endCap; if (middleVertex) { @@ -356,7 +359,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType } if (isSharpCorner && i < len - 1) { - const double nextSegmentLength = util::dist<double>(*currentCoordinate, *nextCoordinate); + const auto nextSegmentLength = util::dist<double>(*currentCoordinate, *nextCoordinate); if (nextSegmentLength > 2 * sharpCornerOffset) { GeometryCoordinate newCurrentVertex = *currentCoordinate + convertPoint<int16_t>(util::round(convertPoint<double>(*nextCoordinate - *currentCoordinate) * (sharpCornerOffset / nextSegmentLength))); distance += util::dist<double>(newCurrentVertex, *currentCoordinate); @@ -458,13 +461,6 @@ void LineBucket::upload(gl::Context& context) { uploaded = true; } -void LineBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderLine(parameters, *this, *layer.as<RenderLineLayer>(), tile); -} - bool LineBucket::hasData() const { return !segments.empty(); } @@ -480,7 +476,7 @@ static float get(const RenderLineLayer& layer, const std::map<std::string, LineP } float LineBucket::getLineWidth(const RenderLineLayer& layer) const { - float lineWidth = layer.evaluated.get<LineWidth>(); + float lineWidth = get<LineWidth>(layer, paintPropertyBinders); float gapWidth = get<LineGapWidth>(layer, paintPropertyBinders); if (gapWidth) { diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp index 95ef2f9a6f..4fb77c377e 100644 --- a/src/mbgl/renderer/line_bucket.hpp +++ b/src/mbgl/renderer/buckets/line_bucket.hpp @@ -19,14 +19,13 @@ class LineBucket : public Bucket { public: LineBucket(const BucketParameters&, const std::vector<const RenderLayer*>&, - const style::LineLayoutProperties&); + const style::LineLayoutProperties::Unevaluated&); void addFeature(const GeometryTileFeature&, const GeometryCollection&) override; bool hasData() const override; void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; float getQueryRadius(const RenderLayer&) const override; @@ -42,7 +41,7 @@ public: std::map<std::string, LineProgram::PaintPropertyBinders> paintPropertyBinders; private: - void addGeometry(const GeometryCoordinates&, FeatureType); + void addGeometry(const GeometryCoordinates&, const GeometryTileFeature&); struct TriangleElement { TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {} @@ -60,6 +59,7 @@ private: std::ptrdiff_t e3; const uint32_t overscaling; + const float zoom; float getLineWidth(const RenderLineLayer& layer) const; }; diff --git a/src/mbgl/renderer/buckets/raster_bucket.cpp b/src/mbgl/renderer/buckets/raster_bucket.cpp new file mode 100644 index 0000000000..a66dd42d74 --- /dev/null +++ b/src/mbgl/renderer/buckets/raster_bucket.cpp @@ -0,0 +1,110 @@ +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/layers/render_raster_layer.hpp> +#include <mbgl/programs/raster_program.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { + +using namespace style; + +RasterBucket::RasterBucket(PremultipliedImage&& image_) { + image = std::make_shared<PremultipliedImage>(std::move(image_)); +} + +RasterBucket::RasterBucket(std::shared_ptr<PremultipliedImage> image_): image(image_) { + +} + +void RasterBucket::upload(gl::Context& context) { + if (!hasData()) { + return; + } + if (!texture) { + texture = context.createTexture(*image); + } + if (!segments.empty()) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(indices)); + } + uploaded = true; +} + +void RasterBucket::clear() { + vertexBuffer = {}; + indexBuffer = {}; + segments.clear(); + vertices.clear(); + indices.clear(); + + uploaded = false; +} + +void RasterBucket::setImage(std::shared_ptr<PremultipliedImage> image_) { + image = std::move(image_); + texture = {}; + uploaded = false; +} + +void RasterBucket::setMask(TileMask&& mask_) { + if (mask == mask_) { + return; + } + + mask = std::move(mask_); + clear(); + + if (mask == TileMask{ { 0, 0, 0 } }) { + // We want to render the full tile, and keeping the segments/vertices/indices empty means + // using the global shared buffers for covering the entire tile. + return; + } + + // Create a new segment so that we will upload (empty) buffers even when there is nothing to + // draw for this tile. + segments.emplace_back(0, 0); + + constexpr const uint16_t vertexLength = 4; + + // Create the vertex buffer for the specified tile mask. + for (const auto& id : mask) { + // Create a quad for every masked tile. + const int32_t vertexExtent = util::EXTENT >> id.z; + + const Point<int16_t> tlVertex = { static_cast<int16_t>(id.x * vertexExtent), + static_cast<int16_t>(id.y * vertexExtent) }; + const Point<int16_t> brVertex = { static_cast<int16_t>(tlVertex.x + vertexExtent), + static_cast<int16_t>(tlVertex.y + vertexExtent) }; + + if (segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { + // Move to a new segments because the old one can't hold the geometry. + segments.emplace_back(vertices.vertexSize(), indices.indexSize()); + } + + vertices.emplace_back( + RasterProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(tlVertex.y) })); + vertices.emplace_back( + RasterProgram::layoutVertex({ brVertex.x, tlVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(tlVertex.y) })); + vertices.emplace_back( + RasterProgram::layoutVertex({ tlVertex.x, brVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(brVertex.y) })); + vertices.emplace_back( + RasterProgram::layoutVertex({ brVertex.x, brVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(brVertex.y) })); + + auto& segment = segments.back(); + assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max()); + const uint16_t offset = segment.vertexLength; + + // 0, 1, 2 + // 1, 2, 3 + indices.emplace_back(offset, offset + 1, offset + 2); + indices.emplace_back(offset + 1, offset + 2, offset + 3); + + segment.vertexLength += vertexLength; + segment.indexLength += 6; + } +} + +bool RasterBucket::hasData() const { + return !!image; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/raster_bucket.hpp b/src/mbgl/renderer/buckets/raster_bucket.hpp new file mode 100644 index 0000000000..3800eadec8 --- /dev/null +++ b/src/mbgl/renderer/buckets/raster_bucket.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/texture.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/programs/raster_program.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/renderer/tile_mask.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/util/optional.hpp> + +namespace mbgl { + +class RasterBucket : public Bucket { +public: + RasterBucket(PremultipliedImage&&); + RasterBucket(std::shared_ptr<PremultipliedImage>); + + void upload(gl::Context&) override; + bool hasData() const override; + + void clear(); + void setImage(std::shared_ptr<PremultipliedImage>); + void setMask(TileMask&&); + + std::shared_ptr<PremultipliedImage> image; + optional<gl::Texture> texture; + TileMask mask{ { 0, 0, 0 } }; + + // Bucket specific vertices are used for Image Sources only + // Raster Tile Sources use the default buffers from Painter + gl::VertexVector<RasterLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> indices; + SegmentVector<RasterAttributes> segments; + + optional<gl::VertexBuffer<RasterLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 9b016c16f9..a3f71f1f6e 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -1,8 +1,8 @@ -#include <mbgl/renderer/symbol_bucket.hpp> -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/text/glyph_atlas.hpp> namespace mbgl { @@ -10,7 +10,8 @@ using namespace style; SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layout_, const std::map<std::string, std::pair< - style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>& layerPaintProperties, + style::IconPaintProperties::PossiblyEvaluated, + style::TextPaintProperties::PossiblyEvaluated>>& layerPaintProperties, const style::DataDrivenPropertyValue<float>& textSize, const style::DataDrivenPropertyValue<float>& iconSize, float zoom, @@ -36,14 +37,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo void SymbolBucket::upload(gl::Context& context) { if (hasTextData()) { text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); + text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw); text.indexBuffer = context.createIndexBuffer(std::move(text.triangles)); - textSizeBinder->upload(context); } if (hasIconData()) { icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); + icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw); icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); - iconSizeBinder->upload(context); } if (!collisionBox.vertices.empty()) { @@ -59,16 +60,8 @@ void SymbolBucket::upload(gl::Context& context) { uploaded = true; } -void SymbolBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderSymbol(parameters, *this, *layer.as<RenderSymbolLayer>(), tile); -} - bool SymbolBucket::hasData() const { - assert(false); // Should be calling SymbolLayout::has{Text,Icon,CollisonBox}Data() instead. - return false; + return hasTextData() || hasIconData() || hasCollisionBoxData(); } bool SymbolBucket::hasTextData() const { diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index a9bc868db0..32f976bcb2 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -15,10 +15,27 @@ namespace mbgl { +class PlacedSymbol { +public: + PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_, + std::array<float, 2> lineOffset_, float placementZoom_, bool useVerticalMode_, GeometryCoordinates line_) : + anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_), + lineOffset(lineOffset_), placementZoom(placementZoom_), useVerticalMode(useVerticalMode_), line(std::move(line_)) {} + Point<float> anchorPoint; + uint16_t segment; + float lowerSize; + float upperSize; + std::array<float, 2> lineOffset; + float placementZoom; + bool useVerticalMode; + GeometryCoordinates line; + std::vector<float> glyphOffsets; +}; + class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated, - const std::map<std::string, std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>&, + const std::map<std::string, std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>>&, const style::DataDrivenPropertyValue<float>& textSize, const style::DataDrivenPropertyValue<float>& iconSize, float zoom, @@ -26,7 +43,6 @@ public: bool iconsNeedLinear); void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; bool hasData() const override; bool hasTextData() const; bool hasIconData() const; @@ -44,10 +60,13 @@ public: struct TextBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices; gl::IndexVector<gl::Triangles> triangles; SegmentVector<SymbolTextAttributes> segments; + std::vector<PlacedSymbol> placedSymbols; optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } text; @@ -55,10 +74,14 @@ public: struct IconBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; + gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices; gl::IndexVector<gl::Triangles> triangles; SegmentVector<SymbolIconAttributes> segments; + std::vector<PlacedSymbol> placedSymbols; + PremultipliedImage atlasImage; optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } icon; @@ -68,10 +91,9 @@ public: SegmentVector<CollisionBoxAttributes> segments; optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer; optional<gl::IndexBuffer<gl::Lines>> indexBuffer; } collisionBox; - - SpriteAtlas* spriteAtlas = nullptr; }; } // namespace mbgl diff --git a/src/mbgl/renderer/cross_faded_property_evaluator.cpp b/src/mbgl/renderer/cross_faded_property_evaluator.cpp index ee3c86614f..4dff9dbf12 100644 --- a/src/mbgl/renderer/cross_faded_property_evaluator.cpp +++ b/src/mbgl/renderer/cross_faded_property_evaluator.cpp @@ -27,7 +27,10 @@ Faded<T> CrossFadedPropertyEvaluator<T>::calculate(const T& min, const T& mid, c const float z = parameters.z; const float fraction = z - std::floor(z); const std::chrono::duration<float> d = parameters.defaultFadeDuration; - const float t = std::min((parameters.now - parameters.zoomHistory.lastIntegerZoomTime) / d, 1.0f); + const float t = + d != std::chrono::duration<float>::zero() + ? std::min((parameters.now - parameters.zoomHistory.lastIntegerZoomTime) / d, 1.0f) + : 1.0f; return z > parameters.zoomHistory.lastIntegerZoom ? Faded<T> { min, mid, 2.0f, 1.0f, fraction + (1.0f - fraction) * t } diff --git a/src/mbgl/renderer/data_driven_property_evaluator.hpp b/src/mbgl/renderer/data_driven_property_evaluator.hpp index 6406b3478b..79ecd0d495 100644 --- a/src/mbgl/renderer/data_driven_property_evaluator.hpp +++ b/src/mbgl/renderer/data_driven_property_evaluator.hpp @@ -24,12 +24,18 @@ public: } ResultType operator()(const style::CameraFunction<T>& function) const { - return ResultType(function.evaluate(parameters.z)); + if (!parameters.useIntegerZoom) { + return ResultType(function.evaluate(parameters.z)); + } else { + return ResultType(function.evaluate(floor(parameters.z))); + } } template <class Function> ResultType operator()(const Function& function) const { - return ResultType(function); + auto returnFunction = function; + returnFunction.useIntegerZoom = parameters.useIntegerZoom; + return ResultType(returnFunction); } private: diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp index 35e246f488..de153b6963 100644 --- a/src/mbgl/renderer/frame_history.cpp +++ b/src/mbgl/renderer/frame_history.cpp @@ -37,9 +37,9 @@ void FrameHistory::record(const TimePoint& now, float zoom, const Duration& dura } for (int16_t z = 0; z <= 255; z++) { - std::chrono::duration<float> timeDiff = now - changeTimes[z]; - int32_t opacityChange = (duration == Milliseconds(0) ? 1 : (timeDiff / duration)) * 255; - uint8_t opacity = z <= zoomIndex + const std::chrono::duration<float> timeDiff = now - changeTimes[z]; + const int32_t opacityChange = (duration == Milliseconds(0) ? 1 : (timeDiff / duration)) * 255; + const uint8_t opacity = z <= zoomIndex ? util::min(255, changeOpacities[z] + opacityChange) : util::max(0, changeOpacities[z] - opacityChange); if (opacities.data[z] != opacity) { @@ -74,4 +74,8 @@ void FrameHistory::bind(gl::Context& context, uint32_t unit) { context.bindTexture(*texture, unit); } +bool FrameHistory::isVisible(const float zoom) const { + return opacities.data[std::floor(zoom * 10)] != 0; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp index f2b11f5f41..75a8b60a71 100644 --- a/src/mbgl/renderer/frame_history.hpp +++ b/src/mbgl/renderer/frame_history.hpp @@ -22,6 +22,7 @@ public: bool needsAnimation(const Duration&) const; void bind(gl::Context&, uint32_t); void upload(gl::Context&, uint32_t); + bool isVisible(const float zoom) const; private: std::array<TimePoint, 256> changeTimes; diff --git a/src/mbgl/renderer/group_by_layout.cpp b/src/mbgl/renderer/group_by_layout.cpp index df1eb7c7dd..3b02727ff8 100644 --- a/src/mbgl/renderer/group_by_layout.cpp +++ b/src/mbgl/renderer/group_by_layout.cpp @@ -19,13 +19,13 @@ std::string layoutKey(const RenderLayer& layer) { writer.StartArray(); writer.Uint(static_cast<uint32_t>(layer.type)); - writer.String(layer.baseImpl.source); - writer.String(layer.baseImpl.sourceLayer); - writer.Double(layer.baseImpl.minZoom); - writer.Double(layer.baseImpl.maxZoom); - writer.Uint(static_cast<uint32_t>(layer.baseImpl.visibility)); - stringify(writer, layer.baseImpl.filter); - layer.baseImpl.stringifyLayout(writer); + writer.String(layer.baseImpl->source); + writer.String(layer.baseImpl->sourceLayer); + writer.Double(layer.baseImpl->minZoom); + writer.Double(layer.baseImpl->maxZoom); + writer.Uint(static_cast<uint32_t>(layer.baseImpl->visibility)); + stringify(writer, layer.baseImpl->filter); + layer.baseImpl->stringifyLayout(writer); writer.EndArray(); return s.GetString(); diff --git a/src/mbgl/renderer/image_atlas.cpp b/src/mbgl/renderer/image_atlas.cpp new file mode 100644 index 0000000000..8eee7c2095 --- /dev/null +++ b/src/mbgl/renderer/image_atlas.cpp @@ -0,0 +1,60 @@ +#include <mbgl/renderer/image_atlas.hpp> + +#include <mapbox/shelf-pack.hpp> + +namespace mbgl { + +static constexpr uint32_t padding = 1; + +ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& image) + : pixelRatio(image.pixelRatio), + textureRect( + bin.x + padding, + bin.y + padding, + bin.w - padding * 2, + bin.h - padding * 2 + ) { +} + +ImageAtlas makeImageAtlas(const ImageMap& images) { + ImageAtlas result; + + mapbox::ShelfPack::ShelfPackOptions options; + options.autoResize = true; + mapbox::ShelfPack pack(0, 0, options); + + for (const auto& entry : images) { + const style::Image::Impl& image = *entry.second; + + const mapbox::Bin& bin = *pack.packOne(-1, + image.image.size.width + 2 * padding, + image.image.size.height + 2 * padding); + + result.image.resize({ + static_cast<uint32_t>(pack.width()), + static_cast<uint32_t>(pack.height()) + }); + + PremultipliedImage::copy(image.image, + result.image, + { 0, 0 }, + { + bin.x + padding, + bin.y + padding + }, + image.image.size); + + result.positions.emplace(image.id, + ImagePosition { bin, image }); + } + + pack.shrink(); + result.image.resize({ + static_cast<uint32_t>(pack.width()), + static_cast<uint32_t>(pack.height()) + }); + + return result; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/image_atlas.hpp b/src/mbgl/renderer/image_atlas.hpp new file mode 100644 index 0000000000..b3cc166eff --- /dev/null +++ b/src/mbgl/renderer/image_atlas.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <mbgl/style/image_impl.hpp> +#include <mbgl/util/rect.hpp> + +#include <mapbox/shelf-pack.hpp> + +#include <array> + +namespace mbgl { + +class ImagePosition { +public: + ImagePosition(const mapbox::Bin&, const style::Image::Impl&); + + float pixelRatio; + Rect<uint16_t> textureRect; + + std::array<uint16_t, 2> tl() const { + return {{ + textureRect.x, + textureRect.y + }}; + } + + std::array<uint16_t, 2> br() const { + return {{ + static_cast<uint16_t>(textureRect.x + textureRect.w), + static_cast<uint16_t>(textureRect.y + textureRect.h) + }}; + } + + std::array<float, 2> displaySize() const { + return {{ + textureRect.w / pixelRatio, + textureRect.h / pixelRatio, + }}; + } +}; + +using ImagePositions = std::map<std::string, ImagePosition>; + +class ImageAtlas { +public: + PremultipliedImage image; + ImagePositions positions; +}; + +ImageAtlas makeImageAtlas(const ImageMap&); + +} // namespace mbgl diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp new file mode 100644 index 0000000000..2ef6be0c4f --- /dev/null +++ b/src/mbgl/renderer/image_manager.cpp @@ -0,0 +1,184 @@ +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { + +void ImageManager::setLoaded(bool loaded_) { + if (loaded == loaded_) { + return; + } + + loaded = loaded_; + + if (loaded) { + for (const auto& entry : requestors) { + notify(*entry.first, entry.second); + } + requestors.clear(); + } +} + +bool ImageManager::isLoaded() const { + return loaded; +} + +void ImageManager::addImage(Immutable<style::Image::Impl> image_) { + assert(images.find(image_->id) == images.end()); + images.emplace(image_->id, std::move(image_)); +} + +void ImageManager::updateImage(Immutable<style::Image::Impl> image_) { + removeImage(image_->id); + addImage(std::move(image_)); +} + +void ImageManager::removeImage(const std::string& id) { + assert(images.find(id) != images.end()); + images.erase(id); + + auto it = patterns.find(id); + if (it != patterns.end()) { + // Clear pattern from the atlas image. + const uint32_t x = it->second.bin->x; + const uint32_t y = it->second.bin->y; + const uint32_t w = it->second.bin->w; + const uint32_t h = it->second.bin->h; + PremultipliedImage::clear(atlasImage, { x, y }, { w, h }); + + shelfPack.unref(*it->second.bin); + patterns.erase(it); + } +} + +const style::Image::Impl* ImageManager::getImage(const std::string& id) const { + const auto it = images.find(id); + if (it != images.end()) { + return it->second.get(); + } + return nullptr; +} + +void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair) { + // If the sprite has been loaded, or if all the icon dependencies are already present + // (i.e. if they've been addeded via runtime styling), then notify the requestor immediately. + // Otherwise, delay notification until the sprite is loaded. At that point, if any of the + // dependencies are still unavailable, we'll just assume they are permanently missing. + bool hasAllDependencies = true; + if (!isLoaded()) { + for (const auto& dependency : pair.first) { + if (images.find(dependency) == images.end()) { + hasAllDependencies = false; + } + } + } + if (isLoaded() || hasAllDependencies) { + notify(requestor, std::move(pair)); + } else { + requestors.emplace(&requestor, std::move(pair)); + } +} + +void ImageManager::removeRequestor(ImageRequestor& requestor) { + requestors.erase(&requestor); +} + +void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const { + ImageMap response; + + for (const auto& dependency : pair.first) { + auto it = images.find(dependency); + if (it != images.end()) { + response.emplace(*it); + } + } + + requestor.onImagesAvailable(response, pair.second); +} + +void ImageManager::dumpDebugLogs() const { + Log::Info(Event::General, "ImageManager::loaded: %d", loaded); +} + +// When copied into the atlas texture, image data is padded by one pixel on each side. Icon +// images are padded with fully transparent pixels, while pattern images are padded with a +// copy of the image data wrapped from the opposite side. In both cases, this ensures the +// correct behavior of GL_LINEAR texture sampling mode. +static constexpr uint16_t padding = 1; + +static mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() { + mapbox::ShelfPack::ShelfPackOptions options; + options.autoResize = true; + return options; +} + +ImageManager::ImageManager() + : shelfPack(64, 64, shelfPackOptions()) { +} + +ImageManager::~ImageManager() = default; + +optional<ImagePosition> ImageManager::getPattern(const std::string& id) { + auto it = patterns.find(id); + if (it != patterns.end()) { + return it->second.position; + } + + const style::Image::Impl* image = getImage(id); + if (!image) { + return {}; + } + + const uint16_t width = image->image.size.width + padding * 2; + const uint16_t height = image->image.size.height + padding * 2; + + mapbox::Bin* bin = shelfPack.packOne(-1, width, height); + if (!bin) { + return {}; + } + + atlasImage.resize(getPixelSize()); + + const PremultipliedImage& src = image->image; + + const uint32_t x = bin->x + padding; + const uint32_t y = bin->y + padding; + const uint32_t w = src.size.width; + const uint32_t h = src.size.height; + + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h }); + + // Add 1 pixel wrapped padding on each side of the image. + PremultipliedImage::copy(src, atlasImage, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y + h }, { w, 1 }); // B + PremultipliedImage::copy(src, atlasImage, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x + w, y }, { 1, h }); // R + + dirty = true; + + return patterns.emplace(id, Pattern { bin, { *bin, *image } }).first->second.position; +} + +Size ImageManager::getPixelSize() const { + return Size { + static_cast<uint32_t>(shelfPack.width()), + static_cast<uint32_t>(shelfPack.height()) + }; +} + +void ImageManager::upload(gl::Context& context, gl::TextureUnit unit) { + if (!atlasTexture) { + atlasTexture = context.createTexture(atlasImage, unit); + } else if (dirty) { + context.updateTexture(*atlasTexture, atlasImage, unit); + } + + dirty = false; +} + +void ImageManager::bind(gl::Context& context, gl::TextureUnit unit) { + upload(context, unit); + context.bindTexture(*atlasTexture, unit, gl::TextureFilter::Linear); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp new file mode 100644 index 0000000000..f72ba9fb53 --- /dev/null +++ b/src/mbgl/renderer/image_manager.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include <mbgl/style/image_impl.hpp> +#include <mbgl/renderer/image_atlas.hpp> +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/gl/texture.hpp> + +#include <mapbox/shelf-pack.hpp> + +#include <set> +#include <string> + +namespace mbgl { + +namespace gl { +class Context; +} // namespace gl + +class ImageRequestor { +public: + virtual ~ImageRequestor() = default; + virtual void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) = 0; +}; + +/* + ImageManager does two things: + + 1. Tracks requests for icon images from tile workers and sends responses when the requests are fulfilled. + 2. Builds a texture atlas for pattern images. + + These are disparate responsibilities and should eventually be handled by different classes. When we implement + data-driven support for `*-pattern`, we'll likely use per-bucket pattern atlases, and that would be a good time + to refactor this. +*/ +class ImageManager : public util::noncopyable { +public: + ImageManager(); + ~ImageManager(); + + void setLoaded(bool); + bool isLoaded() const; + + void dumpDebugLogs() const; + + const style::Image::Impl* getImage(const std::string&) const; + + void addImage(Immutable<style::Image::Impl>); + void updateImage(Immutable<style::Image::Impl>); + void removeImage(const std::string&); + + void getImages(ImageRequestor&, ImageRequestPair&&); + void removeRequestor(ImageRequestor&); + +private: + void notify(ImageRequestor&, const ImageRequestPair&) const; + + bool loaded = false; + + std::unordered_map<ImageRequestor*, ImageRequestPair> requestors; + ImageMap images; + +// Pattern stuff +public: + optional<ImagePosition> getPattern(const std::string& name); + + void bind(gl::Context&, gl::TextureUnit unit); + void upload(gl::Context&, gl::TextureUnit unit); + + Size getPixelSize() const; + + // Only for use in tests. + const PremultipliedImage& getAtlasImage() const { + return atlasImage; + } + +private: + struct Pattern { + mapbox::Bin* bin; + ImagePosition position; + }; + + mapbox::ShelfPack shelfPack; + std::unordered_map<std::string, Pattern> patterns; + PremultipliedImage atlasImage; + mbgl::optional<gl::Texture> atlasTexture; + bool dirty = true; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp new file mode 100644 index 0000000000..9fddba3f74 --- /dev/null +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -0,0 +1,115 @@ +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/style/layers/background_layer_impl.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/util/tile_cover.hpp> + +namespace mbgl { + +using namespace style; + +RenderBackgroundLayer::RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl> _impl) + : RenderLayer(style::LayerType::Background, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::BackgroundLayer::Impl& RenderBackgroundLayer::impl() const { + return static_cast<const style::BackgroundLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderBackgroundLayer::createBucket(const BucketParameters &, + const std::vector<const RenderLayer *> &) const { + assert(false); + return nullptr; +} + +void RenderBackgroundLayer::transition(const TransitionParameters ¶meters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters ¶meters) { + evaluated = unevaluated.evaluate(parameters); + + passes = evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent + : RenderPass::None; +} + +bool RenderBackgroundLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { + // Note that for bottommost layers without a pattern, the background color is drawn with + // glClear rather than this method. + + style::FillPaintProperties::PossiblyEvaluated properties; + properties.get<FillPattern>() = evaluated.get<BackgroundPattern>(); + properties.get<FillOpacity>() = { evaluated.get<BackgroundOpacity>() }; + properties.get<FillColor>() = { evaluated.get<BackgroundColor>() }; + + const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + if (!evaluated.get<BackgroundPattern>().to.empty()) { + optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from); + optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().to); + + if (!imagePosA || !imagePosB) + return; + + parameters.imageManager.bind(parameters.context, 0); + + for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { + parameters.programs.fillPattern.get(properties).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + FillPatternUniforms::values( + parameters.matrixForTile(tileID), + parameters.context.viewport.getCurrentValue().size, + parameters.imageManager.getPixelSize(), + *imagePosA, + *imagePosB, + evaluated.get<BackgroundPattern>(), + tileID, + parameters.state + ), + parameters.staticData.tileVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } else { + for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { + parameters.programs.fill.get(properties).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + FillProgram::UniformValues { + uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) }, + uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_background_layer.hpp b/src/mbgl/renderer/layers/render_background_layer.hpp index b1f709953f..a619670ee4 100644 --- a/src/mbgl/renderer/render_background_layer.hpp +++ b/src/mbgl/renderer/layers/render_background_layer.hpp @@ -1,30 +1,28 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/background_layer.hpp> +#include <mbgl/style/layers/background_layer_impl.hpp> #include <mbgl/style/layers/background_layer_properties.hpp> namespace mbgl { class RenderBackgroundLayer: public RenderLayer { public: - - RenderBackgroundLayer(const style::BackgroundLayer::Impl&); + RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl>); ~RenderBackgroundLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; // Paint properties style::BackgroundPaintProperties::Unevaluated unevaluated; - style::BackgroundPaintProperties::Evaluated evaluated; + style::BackgroundPaintProperties::PossiblyEvaluated evaluated; - const style::BackgroundLayer::Impl* const impl; + const style::BackgroundLayer::Impl& impl() const; }; template <> @@ -32,4 +30,4 @@ inline bool RenderLayer::is<RenderBackgroundLayer>() const { return type == style::LayerType::Background; } -} +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp new file mode 100644 index 0000000000..e7b022f3ee --- /dev/null +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -0,0 +1,120 @@ +#include <mbgl/renderer/layers/render_circle_layer.hpp> +#include <mbgl/renderer/buckets/circle_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/circle_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/circle_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl) + : RenderLayer(style::LayerType::Circle, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::CircleLayer::Impl& RenderCircleLayer::impl() const { + return static_cast<const style::CircleLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderCircleLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<CircleBucket>(parameters, layers); +} + +void RenderCircleLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 || + evaluated.get<style::CircleStrokeWidth>().constantOr(1) > 0) + && (evaluated.get<style::CircleColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::CircleStrokeColor>().constantOr(Color::black()).a > 0) + && (evaluated.get<style::CircleOpacity>().constantOr(1) > 0 || + evaluated.get<style::CircleStrokeOpacity>().constantOr(1) > 0)) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderCircleLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + const bool scaleWithMap = evaluated.get<CirclePitchScale>() == CirclePitchScaleType::Map; + const bool pitchWithMap = evaluated.get<CirclePitchAlignment>() == AlignmentType::Map; + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl))); + CircleBucket& bucket = *reinterpret_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.circle.get(evaluated).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + parameters.mapMode == MapMode::Still + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + CircleProgram::UniformValues { + uniforms::u_matrix::Value{ + tile.translatedMatrix(evaluated.get<CircleTranslate>(), + evaluated.get<CircleTranslateAnchor>(), + parameters.state) + }, + uniforms::u_scale_with_map::Value{ scaleWithMap }, + uniforms::u_extrude_scale::Value{ pitchWithMap + ? std::array<float, 2> {{ + tile.id.pixelsToTileUnits(1, parameters.state.getZoom()), + tile.id.pixelsToTileUnits(1, parameters.state.getZoom()) }} + : parameters.pixelsToGLUnits }, + uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }, + uniforms::u_pitch_with_map::Value{ pitchWithMap } + }, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + } +} + +bool RenderCircleLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float zoom, + const float bearing, + const float pixelsToTileUnits) const { + + // Translate query geometry + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::CircleTranslate>(), + evaluated.get<style::CircleTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + // Evaluate function + auto circleRadius = evaluated.get<style::CircleRadius>() + .evaluate(feature, zoom, style::CircleRadius::defaultValue()) + * pixelsToTileUnits; + + // Test intersection + return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp index 3b82b5c988..f31715f98f 100644 --- a/src/mbgl/renderer/render_circle_layer.hpp +++ b/src/mbgl/renderer/layers/render_circle_layer.hpp @@ -1,22 +1,20 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/style/layers/circle_layer_impl.hpp> #include <mbgl/style/layers/circle_layer_properties.hpp> namespace mbgl { class RenderCircleLayer: public RenderLayer { public: - - RenderCircleLayer(const style::CircleLayer::Impl&); + RenderCircleLayer(Immutable<style::CircleLayer::Impl>); ~RenderCircleLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,9 +27,9 @@ public: // Paint properties style::CirclePaintProperties::Unevaluated unevaluated; - style::CirclePaintProperties::Evaluated evaluated; + style::CirclePaintProperties::PossiblyEvaluated evaluated; - const style::CircleLayer::Impl* const impl; + const style::CircleLayer::Impl& impl() const; }; template <> @@ -39,4 +37,4 @@ inline bool RenderLayer::is<RenderCircleLayer>() const { return type == style::LayerType::Circle; } -} +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp new file mode 100644 index 0000000000..7ece3970da --- /dev/null +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -0,0 +1,81 @@ +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/style/layers/custom_layer_impl.hpp> +#include <mbgl/map/transform_state.hpp> + +namespace mbgl { + +using namespace style; + +RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl) + : RenderLayer(LayerType::Custom, _impl) { +} + +RenderCustomLayer::~RenderCustomLayer() { + assert(BackendScope::exists()); + if (initialized) { + if (contextDestroyed && impl().contextLostFn ) { + impl().contextLostFn(impl().context); + } else if (!contextDestroyed && impl().deinitializeFn) { + impl().deinitializeFn(impl().context); + } + } +} + +const CustomLayer::Impl& RenderCustomLayer::impl() const { + return static_cast<const CustomLayer::Impl&>(*baseImpl); +} + +void RenderCustomLayer::evaluate(const PropertyEvaluationParameters&) { + passes = RenderPass::Translucent; +} + +bool RenderCustomLayer::hasTransition() const { + return false; +} + +std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); + return nullptr; +} + +void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) { + if (!initialized) { + assert(impl().initializeFn); + impl().initializeFn(impl().context); + initialized = true; + } + + gl::Context& context = paintParameters.context; + const TransformState& state = paintParameters.state; + + // Reset GL state to a known state so the CustomLayer always has a clean slate. + context.bindVertexArray = 0; + context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)); + context.setStencilMode(gl::StencilMode::disabled()); + context.setColorMode(paintParameters.colorModeForRenderPass()); + + CustomLayerRenderParameters parameters; + + parameters.width = state.getSize().width; + parameters.height = state.getSize().height; + parameters.latitude = state.getLatLng().latitude(); + parameters.longitude = state.getLatLng().longitude(); + parameters.zoom = state.getZoom(); + parameters.bearing = -state.getAngle() * util::RAD2DEG; + parameters.pitch = state.getPitch(); + parameters.fieldOfView = state.getFieldOfView(); + + assert(impl().renderFn); + impl().renderFn(impl().context, parameters); + + // Reset the view back to our original one, just in case the CustomLayer changed + // the viewport or Framebuffer. + paintParameters.backend.bind(); + context.setDirtyState(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp index c3af6c77b2..32ed9da8da 100644 --- a/src/mbgl/renderer/render_custom_layer.hpp +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -1,25 +1,31 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/custom_layer.hpp> +#include <mbgl/style/layers/custom_layer_impl.hpp> namespace mbgl { class RenderCustomLayer: public RenderLayer { public: + RenderCustomLayer(Immutable<style::CustomLayer::Impl>); + ~RenderCustomLayer() final; - RenderCustomLayer(const style::CustomLayer::Impl&); - ~RenderCustomLayer() final = default; - - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) final {} + void transition(const TransitionParameters&) final {} void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const final; + void render(PaintParameters&, RenderSource*) final; + + const style::CustomLayer::Impl& impl() const; - const style::CustomLayer::Impl* const impl; + void markContextDestroyed() { + contextDestroyed = true; + }; + +private: + bool initialized = false; + bool contextDestroyed = false; }; template <> @@ -27,4 +33,4 @@ inline bool RenderLayer::is<RenderCustomLayer>() const { return type == style::LayerType::Custom; } -} +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp new file mode 100644 index 0000000000..fbd6160e8a --- /dev/null +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -0,0 +1,167 @@ +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl) + : RenderLayer(style::LayerType::FillExtrusion, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::FillExtrusionLayer::Impl& RenderFillExtrusionLayer::impl() const { + return static_cast<const style::FillExtrusionLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<FillExtrusionBucket>(parameters, layers); +} + +void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) + ? (RenderPass::Translucent | RenderPass::Pass3D) + : RenderPass::None; +} + +bool RenderFillExtrusionLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + if (parameters.pass == RenderPass::Pass3D) { + const auto& size = parameters.staticData.backendSize; + + if (!renderTexture || renderTexture->getSize() != size) { + renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer); + } + + renderTexture->bind(); + + optional<float> depthClearValue = {}; + if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0; + // Flag the depth buffer as no longer needing to be cleared for the remainder of this pass. + parameters.staticData.depthRenderbuffer->shouldClear(false); + + parameters.context.setStencilMode(gl::StencilMode::disabled()); + parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {}); + + if (evaluated.get<FillExtrusionPattern>().from.empty()) { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); + FillExtrusionBucket& bucket = + *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.fillExtrusion.get(evaluated).draw( + parameters.context, gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + FillExtrusionUniforms::values( + tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), + evaluated.get<FillExtrusionTranslateAnchor>(), + parameters.state), + parameters.state, parameters.evaluatedLight), + *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, + bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), + getID()); + } + } else { + optional<ImagePosition> imagePosA = + parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from); + optional<ImagePosition> imagePosB = + parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to); + + if (!imagePosA || !imagePosB) { + return; + } + + parameters.imageManager.bind(parameters.context, 0); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); + FillExtrusionBucket& bucket = + *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.fillExtrusionPattern.get(evaluated).draw( + parameters.context, gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + FillExtrusionPatternUniforms::values( + tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), + evaluated.get<FillExtrusionTranslateAnchor>(), + parameters.state), + parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB, + evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state, + -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, + parameters.evaluatedLight), + *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, + bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), + getID()); + } + } + + } else if (parameters.pass == RenderPass::Translucent) { + parameters.context.bindTexture(renderTexture->getTexture()); + + const auto& size = parameters.staticData.backendSize; + + mat4 viewportMat; + matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); + + const Properties<>::PossiblyEvaluated properties; + + parameters.programs.extrusionTexture.draw( + parameters.context, gl::Triangles(), gl::DepthMode::disabled(), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + ExtrusionTextureProgram::UniformValues{ + uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, + uniforms::u_image::Value{ 0 }, + uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } }, + parameters.staticData.extrusionTextureVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.extrusionTextureSegments, + ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, + parameters.state.getZoom(), getID()); + } +} + +bool RenderFillExtrusionLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float, + const float bearing, + const float pixelsToTileUnits) const { + + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::FillExtrusionTranslate>(), + evaluated.get<style::FillExtrusionTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp index bd66d8e3b1..838494cf91 100644 --- a/src/mbgl/renderer/render_fill_extrusion_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp @@ -1,22 +1,22 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> #include <mbgl/style/layers/fill_extrusion_layer_properties.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/offscreen_texture.hpp> namespace mbgl { class RenderFillExtrusionLayer: public RenderLayer { public: - - RenderFillExtrusionLayer(const style::FillExtrusionLayer::Impl&); + RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl>); ~RenderFillExtrusionLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,9 +29,11 @@ public: // Paint properties style::FillExtrusionPaintProperties::Unevaluated unevaluated; - style::FillExtrusionPaintProperties::Evaluated evaluated; + style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated; + + const style::FillExtrusionLayer::Impl& impl() const; - const style::FillExtrusionLayer::Impl* const impl; + optional<OffscreenTexture> renderTexture; }; template <> diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp new file mode 100644 index 0000000000..22cb9563c1 --- /dev/null +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -0,0 +1,205 @@ +#include <mbgl/renderer/layers/render_fill_layer.hpp> +#include <mbgl/renderer/buckets/fill_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/fill_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl) + : RenderLayer(style::LayerType::Fill, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::FillLayer::Impl& RenderFillLayer::impl() const { + return static_cast<const style::FillLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderFillLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<FillBucket>(parameters, layers); +} + +void RenderFillLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + if (unevaluated.get<style::FillOutlineColor>().isUndefined()) { + evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>(); + } + + passes = RenderPass::None; + + if (evaluated.get<style::FillAntialias>()) { + passes |= RenderPass::Translucent; + } + + if (!unevaluated.get<style::FillPattern>().isUndefined() + || evaluated.get<style::FillColor>().constantOr(Color()).a < 1.0f + || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f) { + passes |= RenderPass::Translucent; + } else { + passes |= RenderPass::Opaque; + } +} + +bool RenderFillLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { + if (evaluated.get<FillPattern>().from.empty()) { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl))); + FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, + const auto& drawMode, + const auto& depthMode, + const auto& indexBuffer, + const auto& segments) { + program.get(evaluated).draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + FillProgram::UniformValues { + uniforms::u_matrix::Value{ + tile.translatedMatrix(evaluated.get<FillTranslate>(), + evaluated.get<FillTranslateAnchor>(), + parameters.state) + }, + uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + }, + *bucket.vertexBuffer, + indexBuffer, + segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + // Only draw the fill when it's opaque and we're drawing opaque fragments, + // or when it's translucent and we're drawing translucent fragments. + if ((evaluated.get<FillColor>().constantOr(Color()).a >= 1.0f + && evaluated.get<FillOpacity>().constantOr(0) >= 1.0f) == (parameters.pass == RenderPass::Opaque)) { + draw(parameters.programs.fill, + gl::Triangles(), + parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque + ? gl::DepthMode::ReadWrite + : gl::DepthMode::ReadOnly), + *bucket.triangleIndexBuffer, + bucket.triangleSegments); + } + + if (evaluated.get<FillAntialias>() && parameters.pass == RenderPass::Translucent) { + draw(parameters.programs.fillOutline, + gl::Lines{ 2.0f }, + parameters.depthModeForSublayer( + unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0, + gl::DepthMode::ReadOnly), + *bucket.lineIndexBuffer, + bucket.lineSegments); + } + } + } else { + if (parameters.pass != RenderPass::Translucent) { + return; + } + + optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillPattern>().from); + optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillPattern>().to); + + if (!imagePosA || !imagePosB) { + return; + } + + parameters.imageManager.bind(parameters.context, 0); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl))); + FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, + const auto& drawMode, + const auto& depthMode, + const auto& indexBuffer, + const auto& segments) { + program.get(evaluated).draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + FillPatternUniforms::values( + tile.translatedMatrix(evaluated.get<FillTranslate>(), + evaluated.get<FillTranslateAnchor>(), + parameters.state), + parameters.context.viewport.getCurrentValue().size, + parameters.imageManager.getPixelSize(), + *imagePosA, + *imagePosB, + evaluated.get<FillPattern>(), + tile.id, + parameters.state + ), + *bucket.vertexBuffer, + indexBuffer, + segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + draw(parameters.programs.fillPattern, + gl::Triangles(), + parameters.depthModeForSublayer(1, gl::DepthMode::ReadWrite), + *bucket.triangleIndexBuffer, + bucket.triangleSegments); + + if (evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined()) { + draw(parameters.programs.fillOutlinePattern, + gl::Lines { 2.0f }, + parameters.depthModeForSublayer(2, gl::DepthMode::ReadOnly), + *bucket.lineIndexBuffer, + bucket.lineSegments); + } + } + } +} + +bool RenderFillLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float, + const float bearing, + const float pixelsToTileUnits) const { + + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::FillTranslate>(), + evaluated.get<style::FillTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); +} + + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp index 8080cf289b..a51865698f 100644 --- a/src/mbgl/renderer/render_fill_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_layer.hpp @@ -1,22 +1,20 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_layer_impl.hpp> #include <mbgl/style/layers/fill_layer_properties.hpp> namespace mbgl { class RenderFillLayer: public RenderLayer { public: - - RenderFillLayer(const style::FillLayer::Impl&); + RenderFillLayer(Immutable<style::FillLayer::Impl>); ~RenderFillLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,9 +27,9 @@ public: // Paint properties style::FillPaintProperties::Unevaluated unevaluated; - style::FillPaintProperties::Evaluated evaluated; + style::FillPaintProperties::PossiblyEvaluated evaluated; - const style::FillLayer::Impl* const impl; + const style::FillLayer::Impl& impl() const; }; template <> diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp new file mode 100644 index 0000000000..1b4a1c0ff7 --- /dev/null +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -0,0 +1,204 @@ +#include <mbgl/renderer/layers/render_line_layer.hpp> +#include <mbgl/renderer/buckets/line_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/line_program.hpp> +#include <mbgl/geometry/line_atlas.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl) + : RenderLayer(style::LayerType::Line, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::LineLayer::Impl& RenderLineLayer::impl() const { + return static_cast<const style::LineLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<LineBucket>(parameters, layers, impl().layout); +} + +void RenderLineLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) { + style::Properties<LineFloorwidth>::Unevaluated extra(unevaluated.get<style::LineWidth>()); + + auto dashArrayParams = parameters; + dashArrayParams.useIntegerZoom = true; + + evaluated = RenderLinePaintProperties::PossiblyEvaluated( + unevaluated.evaluate(parameters).concat(extra.evaluate(dashArrayParams))); + + passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0 + && evaluated.get<style::LineColor>().constantOr(Color::black()).a > 0 + && evaluated.get<style::LineWidth>().constantOr(1.0) > 0) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderLineLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<LineBucket*>(tile.tile.getBucket(*baseImpl))); + LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, auto&& uniformValues) { + program.get(evaluated).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + std::move(uniformValues), + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + if (!evaluated.get<LineDasharray>().from.empty()) { + const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round + ? LinePatternCap::Round : LinePatternCap::Square; + LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().from, cap); + LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().to, cap); + + parameters.lineAtlas.bind(parameters.context, 0); + + draw(parameters.programs.lineSDF, + LineSDFProgram::uniformValues( + evaluated, + parameters.pixelRatio, + tile, + parameters.state, + parameters.pixelsToGLUnits, + posA, + posB, + parameters.lineAtlas.getSize().width)); + + } else if (!evaluated.get<LinePattern>().from.empty()) { + optional<ImagePosition> posA = parameters.imageManager.getPattern(evaluated.get<LinePattern>().from); + optional<ImagePosition> posB = parameters.imageManager.getPattern(evaluated.get<LinePattern>().to); + + if (!posA || !posB) + return; + + parameters.imageManager.bind(parameters.context, 0); + + draw(parameters.programs.linePattern, + LinePatternProgram::uniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits, + parameters.imageManager.getPixelSize(), + *posA, + *posB)); + + } else { + draw(parameters.programs.line, + LineProgram::uniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits)); + } + } +} + +optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) { + if (offset == 0) return {}; + + GeometryCollection newRings; + Point<double> zero(0, 0); + for (const auto& ring : rings) { + newRings.emplace_back(); + auto& newRing = newRings.back(); + + for (auto i = ring.begin(); i != ring.end(); i++) { + auto& p = *i; + + Point<double> aToB = i == ring.begin() ? + zero : + util::perp(util::unit(convertPoint<double>(p - *(i - 1)))); + Point<double> bToC = i + 1 == ring.end() ? + zero : + util::perp(util::unit(convertPoint<double>(*(i + 1) - p))); + Point<double> extrude = util::unit(aToB + bToC); + + const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y; + extrude *= (1.0 / cosHalfAngle); + + newRing.push_back(convertPoint<int16_t>(extrude * offset) + p); + } + } + + return newRings; +} + +bool RenderLineLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float zoom, + const float bearing, + const float pixelsToTileUnits) const { + + // Translate query geometry + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::LineTranslate>(), + evaluated.get<style::LineTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + // Evaluate function + auto offset = evaluated.get<style::LineOffset>() + .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits; + + // Apply offset to geometry + auto offsetGeometry = offsetLine(feature.getGeometries(), offset); + + // Test intersection + const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits; + return util::polygonIntersectsBufferedMultiLine( + translatedQueryGeometry.value_or(queryGeometry), + offsetGeometry.value_or(feature.getGeometries()), + halfWidth); +} + +float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { + float lineWidth = evaluated.get<style::LineWidth>() + .evaluate(feature, zoom, style::LineWidth::defaultValue()); + float gapWidth = evaluated.get<style::LineGapWidth>() + .evaluate(feature, zoom, style::LineGapWidth::defaultValue()); + if (gapWidth) { + return gapWidth + 2 * lineWidth; + } else { + return lineWidth; + } +} + + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 6d6fecc227..8bf7e2329d 100644 --- a/src/mbgl/renderer/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -1,22 +1,29 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/style/layers/line_layer_properties.hpp> +#include <mbgl/programs/uniforms.hpp> namespace mbgl { +struct LineFloorwidth : style::DataDrivenPaintProperty<float, attributes::a_floorwidth, uniforms::u_floorwidth> { + static float defaultValue() { return 1; } +}; + +class RenderLinePaintProperties : public style::ConcatenateProperties< + style::LinePaintProperties::PropertyTypes, + TypeList<LineFloorwidth>>::Type {}; + class RenderLineLayer: public RenderLayer { public: - - RenderLineLayer(const style::LineLayer::Impl&); + RenderLineLayer(Immutable<style::LineLayer::Impl>); ~RenderLineLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; bool queryIntersectsFeature( const GeometryCoordinates&, @@ -29,16 +36,12 @@ public: // Paint properties style::LinePaintProperties::Unevaluated unevaluated; - style::LinePaintProperties::Evaluated evaluated; - - const style::LineLayer::Impl* const impl; + RenderLinePaintProperties::PossiblyEvaluated evaluated; - // Special case - float dashLineWidth = 1; + const style::LineLayer::Impl& impl() const; private: float getLineWidth(const GeometryTileFeature&, const float) const; - }; template <> diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp new file mode 100644 index 0000000000..06616d90e5 --- /dev/null +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -0,0 +1,155 @@ +#include <mbgl/renderer/layers/render_raster_layer.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/sources/render_image_source.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/raster_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/raster_layer_impl.hpp> + +namespace mbgl { + +using namespace style; + +RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl) + : RenderLayer(style::LayerType::Raster, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::RasterLayer::Impl& RenderRasterLayer::impl() const { + return static_cast<const style::RasterLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderRasterLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); + return nullptr; +} + +void RenderRasterLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderRasterLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +static float saturationFactor(float saturation) { + if (saturation > 0) { + return 1 - 1 / (1.001 - saturation); + } else { + return -saturation; + } +} + +static float contrastFactor(float contrast) { + if (contrast > 0) { + return 1 / (1 - contrast); + } else { + return 1 + contrast; + } +} + +static std::array<float, 3> spinWeights(float spin) { + spin *= util::DEG2RAD; + float s = std::sin(spin); + float c = std::cos(spin); + std::array<float, 3> spin_weights = {{ + (2 * c + 1) / 3, + (-std::sqrt(3.0f) * s - c + 1) / 3, + (std::sqrt(3.0f) * s - c + 1) / 3 + }}; + return spin_weights; +} + +void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source) { + if (parameters.pass != RenderPass::Translucent) + return; + + auto draw = [&] (const mat4& matrix, + const auto& vertexBuffer, + const auto& indexBuffer, + const auto& segments) { + parameters.programs.raster.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + RasterProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_image0::Value{ 0 }, + uniforms::u_image1::Value{ 1 }, + uniforms::u_opacity::Value{ evaluated.get<RasterOpacity>() }, + uniforms::u_fade_t::Value{ 1 }, + uniforms::u_brightness_low::Value{ evaluated.get<RasterBrightnessMin>() }, + uniforms::u_brightness_high::Value{ evaluated.get<RasterBrightnessMax>() }, + uniforms::u_saturation_factor::Value{ saturationFactor(evaluated.get<RasterSaturation>()) }, + uniforms::u_contrast_factor::Value{ contrastFactor(evaluated.get<RasterContrast>()) }, + uniforms::u_spin_weights::Value{ spinWeights(evaluated.get<RasterHueRotate>()) }, + uniforms::u_buffer_scale::Value{ 1.0f }, + uniforms::u_scale_parent::Value{ 1.0f }, + uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} }, + }, + vertexBuffer, + indexBuffer, + segments, + RasterProgram::PaintPropertyBinders { evaluated, 0 }, + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + if (RenderImageSource* imageSource = source->as<RenderImageSource>()) { + if (imageSource->isEnabled() && imageSource->isLoaded() && !imageSource->bucket->needsUpload()) { + RasterBucket& bucket = *imageSource->bucket; + + assert(bucket.texture); + parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); + parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); + + for (auto matrix_ : imageSource->matrices) { + draw(matrix_, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments); + } + } + } else { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl))); + RasterBucket& bucket = *reinterpret_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl)); + + if (!bucket.hasData()) + continue; + + assert(bucket.texture); + parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); + parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); + + if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) { + // Draw only the parts of the tile that aren't drawn by another tile in the layer. + draw(tile.matrix, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments); + } else { + // Draw the full tile. + draw(tile.matrix, + parameters.staticData.rasterVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.rasterSegments); + } + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_raster_layer.hpp b/src/mbgl/renderer/layers/render_raster_layer.hpp index 3ffeb8febf..87de316f7c 100644 --- a/src/mbgl/renderer/render_raster_layer.hpp +++ b/src/mbgl/renderer/layers/render_raster_layer.hpp @@ -1,30 +1,29 @@ #pragma once #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/style/layers/raster_layer.hpp> +#include <mbgl/style/layers/raster_layer_impl.hpp> #include <mbgl/style/layers/raster_layer_properties.hpp> namespace mbgl { class RenderRasterLayer: public RenderLayer { public: - - RenderRasterLayer(const style::RasterLayer::Impl&); + RenderRasterLayer(Immutable<style::RasterLayer::Impl>); ~RenderRasterLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; // Paint properties style::RasterPaintProperties::Unevaluated unevaluated; - style::RasterPaintProperties::Evaluated evaluated; + style::RasterPaintProperties::PossiblyEvaluated evaluated; - const style::RasterLayer::Impl* const impl; + const style::RasterLayer::Impl& impl() const; }; template <> diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp new file mode 100644 index 0000000000..1376e8a3d8 --- /dev/null +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -0,0 +1,325 @@ +#include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/renderer/property_evaluation_parameters.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/frame_history.hpp> +#include <mbgl/text/glyph_atlas.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/symbol_program.hpp> +#include <mbgl/programs/collision_box_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/tile/geometry_tile.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/layout/symbol_layout.hpp> +#include <mbgl/layout/symbol_projection.hpp> +#include <mbgl/util/math.hpp> + +#include <cmath> + +namespace mbgl { + +using namespace style; + +RenderSymbolLayer::RenderSymbolLayer(Immutable<style::SymbolLayer::Impl> _impl) + : RenderLayer(style::LayerType::Symbol, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::SymbolLayer::Impl& RenderSymbolLayer::impl() const { + return static_cast<const style::SymbolLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderSymbolLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); // Should be calling createLayout() instead. + return nullptr; +} + +std::unique_ptr<SymbolLayout> RenderSymbolLayer::createLayout(const BucketParameters& parameters, + const std::vector<const RenderLayer*>& group, + std::unique_ptr<GeometryTileLayer> layer, + GlyphDependencies& glyphDependencies, + ImageDependencies& imageDependencies) const { + return std::make_unique<SymbolLayout>(parameters, + group, + std::move(layer), + imageDependencies, + glyphDependencies); +} + +void RenderSymbolLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + auto hasIconOpacity = evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0; + auto hasTextOpacity = evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0; + + passes = ((evaluated.get<style::IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) + || (evaluated.get<style::TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderSymbolLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl))); + SymbolBucket& bucket = *reinterpret_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl)); + + const auto& layout = bucket.layout; + + parameters.frameHistory.bind(parameters.context, 1); + + auto draw = [&] (auto& program, + auto&& uniformValues, + const auto& buffers, + const auto& symbolSizeBinder, + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties) + { + // We clip symbols to their tile extent in still mode. + const bool needsClipping = parameters.mapMode == MapMode::Still; + + program.get(paintProperties).draw( + parameters.context, + gl::Triangles(), + values_.pitchAlignment == AlignmentType::Map + ? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly) + : gl::DepthMode::disabled(), + needsClipping + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + std::move(uniformValues), + *buffers.vertexBuffer, + *buffers.dynamicVertexBuffer, + *symbolSizeBinder, + *buffers.indexBuffer, + buffers.segments, + binders, + paintProperties, + parameters.state.getZoom(), + getID() + ); + }; + + assert(dynamic_cast<GeometryTile*>(&tile.tile)); + GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile); + + if (bucket.hasIconData()) { + auto values = iconPropertyValues(layout); + auto paintPropertyValues = iconPaintProperties(); + + const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line && + layout.get<IconRotationAlignment>() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.icon.dynamicVertices, + bucket.icon.placedSymbols, + tile.matrix, + values, + tile, + *bucket.iconSizeBinder, + parameters.state, + parameters.frameHistory); + + parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices)); + } + + const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear; + const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0; + + parameters.context.bindTexture(*geometryTile.iconAtlasTexture, 0, + bucket.sdfIcons || parameters.state.isChanging() || iconScaled || iconTransformed + ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest); + + const Size texsize = geometryTile.iconAtlasTexture->size; + + if (bucket.sdfIcons) { + if (values.hasHalo) { + draw(parameters.programs.symbolIconSDF, + SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + + if (values.hasFill) { + draw(parameters.programs.symbolIconSDF, + SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + } else { + draw(parameters.programs.symbolIcon, + SymbolIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + } + + if (bucket.hasTextData()) { + parameters.context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear); + + auto values = textPropertyValues(layout); + auto paintPropertyValues = textPaintProperties(); + + const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line && + layout.get<TextRotationAlignment>() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.text.dynamicVertices, + bucket.text.placedSymbols, + tile.matrix, + values, + tile, + *bucket.textSizeBinder, + parameters.state, + parameters.frameHistory); + + parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); + } + + const Size texsize = geometryTile.glyphAtlasTexture->size; + + if (values.hasHalo) { + draw(parameters.programs.symbolGlyph, + SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo), + bucket.text, + bucket.textSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).second, + paintPropertyValues); + } + + if (values.hasFill) { + draw(parameters.programs.symbolGlyph, + SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill), + bucket.text, + bucket.textSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).second, + paintPropertyValues); + } + } + + if (bucket.hasCollisionBoxData()) { + static const style::Properties<>::PossiblyEvaluated properties {}; + static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0); + + parameters.programs.collisionBox.draw( + parameters.context, + gl::Lines { 1.0f }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + CollisionBoxProgram::UniformValues { + uniforms::u_matrix::Value{ tile.matrix }, + uniforms::u_scale::Value{ std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ)) }, + uniforms::u_zoom::Value{ float(parameters.state.getZoom() * 10) }, + uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) }, + uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, + uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }, + uniforms::u_pitch::Value{ parameters.state.getPitch() }, + uniforms::u_fadetexture::Value{ 1 } + }, + *bucket.collisionBox.vertexBuffer, + *bucket.collisionBox.indexBuffer, + bucket.collisionBox.segments, + paintAttributeData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } +} + +style::IconPaintProperties::PossiblyEvaluated RenderSymbolLayer::iconPaintProperties() const { + return style::IconPaintProperties::PossiblyEvaluated { + evaluated.get<style::IconOpacity>(), + evaluated.get<style::IconColor>(), + evaluated.get<style::IconHaloColor>(), + evaluated.get<style::IconHaloWidth>(), + evaluated.get<style::IconHaloBlur>(), + evaluated.get<style::IconTranslate>(), + evaluated.get<style::IconTranslateAnchor>() + }; +} + +style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProperties() const { + return style::TextPaintProperties::PossiblyEvaluated { + evaluated.get<style::TextOpacity>(), + evaluated.get<style::TextColor>(), + evaluated.get<style::TextHaloColor>(), + evaluated.get<style::TextHaloWidth>(), + evaluated.get<style::TextHaloBlur>(), + evaluated.get<style::TextTranslate>(), + evaluated.get<style::TextTranslateAnchor>() + }; +} + + +style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { + return style::SymbolPropertyValues { + layout_.get<style::IconPitchAlignment>(), + layout_.get<style::IconRotationAlignment>(), + layout_.get<style::IconKeepUpright>(), + evaluated.get<style::IconTranslate>(), + evaluated.get<style::IconTranslateAnchor>(), + evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 && + evaluated.get<style::IconHaloWidth>().constantOr(1), + evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0, + 10.0f + }; +} + +style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { + // We hide line labels with viewport alignment as they move into the distance + // because the approximations we use for drawing their glyphs get progressively worse + // The "1.5" here means we start hiding them when the distance from the label + // to the camera is 50% greater than the distance from the center of the map + // to the camera. Depending on viewport properties, you might expect this to filter + // the top third of the screen at pitch 60, and do almost nothing at pitch 45 + // "10" is effectively infinite at any pitch we support + const bool limitMaxDistance = + layout_.get<style::SymbolPlacement>() == style::SymbolPlacementType::Line + && layout_.get<style::TextRotationAlignment>() == style::AlignmentType::Map + && layout_.get<style::TextPitchAlignment>() == style::AlignmentType::Viewport; + + return style::SymbolPropertyValues { + layout_.get<style::TextPitchAlignment>(), + layout_.get<style::TextRotationAlignment>(), + layout_.get<style::TextKeepUpright>(), + evaluated.get<style::TextTranslate>(), + evaluated.get<style::TextTranslateAnchor>(), + evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 && + evaluated.get<style::TextHaloWidth>().constantOr(1), + evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0, + limitMaxDistance ? 1.5f : 10.0f + }; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp index 80ffd95a06..83709b5122 100644 --- a/src/mbgl/renderer/render_symbol_layer.hpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp @@ -2,8 +2,8 @@ #include <mbgl/text/glyph.hpp> #include <mbgl/renderer/render_layer.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/style/layers/symbol_layer.hpp> +#include <mbgl/style/image_impl.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> namespace mbgl { @@ -13,7 +13,7 @@ namespace style { // {icon,text}-specific paint-property packs for use in the symbol Programs. // Since each program deals either with icons or text, using a smaller property set // lets us avoid unnecessarily binding attributes for properties the program wouldn't use. -class IconPaintProperties : public PaintProperties< +class IconPaintProperties : public Properties< IconOpacity, IconColor, IconHaloColor, @@ -23,7 +23,7 @@ class IconPaintProperties : public PaintProperties< IconTranslateAnchor > {}; -class TextPaintProperties : public PaintProperties< +class TextPaintProperties : public Properties< TextOpacity, TextColor, TextHaloColor, @@ -40,17 +40,16 @@ public: // Layout AlignmentType pitchAlignment; AlignmentType rotationAlignment; - PossiblyEvaluatedPropertyValue<float> layoutSize; + bool keepUpright; // Paint std::array<float, 2> translate; TranslateAnchorType translateAnchor; - float paintSize; - - float sdfScale; // Constant (1.0 or 24.0) bool hasHalo; bool hasFill; + + float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else }; } // namespace style @@ -61,33 +60,35 @@ class GeometryTileLayer; class RenderSymbolLayer: public RenderLayer { public: - RenderSymbolLayer(const style::SymbolLayer::Impl&); + RenderSymbolLayer(Immutable<style::SymbolLayer::Impl>); ~RenderSymbolLayer() final = default; - std::unique_ptr<RenderLayer> clone() const override; - - void cascade(const CascadeParameters&) override; + void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; - style::IconPaintProperties::Evaluated iconPaintProperties() const; - style::TextPaintProperties::Evaluated textPaintProperties() const; + style::IconPaintProperties::PossiblyEvaluated iconPaintProperties() const; + style::TextPaintProperties::PossiblyEvaluated textPaintProperties() const; style::SymbolPropertyValues iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const; style::SymbolPropertyValues textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; - std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, const std::vector<const RenderLayer*>&, - const GeometryTileLayer&, GlyphDependencies&, IconDependencies&) const; + std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, + const std::vector<const RenderLayer*>&, + std::unique_ptr<GeometryTileLayer>, + GlyphDependencies&, + ImageDependencies&) const; // Paint properties style::SymbolPaintProperties::Unevaluated unevaluated; - style::SymbolPaintProperties::Evaluated evaluated; + style::SymbolPaintProperties::PossiblyEvaluated evaluated; float iconSize = 1.0f; float textSize = 16.0f; - const style::SymbolLayer::Impl* const impl; + const style::SymbolLayer::Impl& impl() const; }; template <> diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp new file mode 100644 index 0000000000..299db844bc --- /dev/null +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -0,0 +1,98 @@ +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/update_parameters.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/map/transform_state.hpp> + +namespace mbgl { + +PaintParameters::PaintParameters(gl::Context& context_, + float pixelRatio_, + GLContextMode contextMode_, + RendererBackend& backend_, + const UpdateParameters& updateParameters, + const EvaluatedLight& evaluatedLight_, + RenderStaticData& staticData_, + FrameHistory& frameHistory_, + ImageManager& imageManager_, + LineAtlas& lineAtlas_) + : context(context_), + backend(backend_), + state(updateParameters.transformState), + evaluatedLight(evaluatedLight_), + staticData(staticData_), + frameHistory(frameHistory_), + imageManager(imageManager_), + lineAtlas(lineAtlas_), + mapMode(updateParameters.mode), + debugOptions(updateParameters.debugOptions), + contextMode(contextMode_), + timePoint(updateParameters.timePoint), + pixelRatio(pixelRatio_), +#ifndef NDEBUG + programs((debugOptions & MapDebugOptions::Overdraw) ? staticData_.overdrawPrograms : staticData_.programs) +#else + programs(staticData_.programs) +#endif +{ + // Update the default matrices to the current viewport dimensions. + state.getProjMatrix(projMatrix); + + // Calculate a second projection matrix with the near plane clipped to 100 so as + // not to waste lots of depth buffer precision on very close empty space, for layer + // types (fill-extrusion) that use the depth buffer to emulate real-world space. + state.getProjMatrix(nearClippedProjMatrix, 100); + + pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }}; + + if (state.getViewportMode() == ViewportMode::FlippedY) { + pixelsToGLUnits[1] *= -1; + } +} + +mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID) { + mat4 matrix; + state.matrixFor(matrix, tileID); + matrix::multiply(matrix, projMatrix, matrix); + return matrix; +} + +gl::DepthMode PaintParameters::depthModeForSublayer(uint8_t n, gl::DepthMode::Mask mask) const { + float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon; + float farDepth = nearDepth + depthRangeSize; + return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } }; +} + +gl::DepthMode PaintParameters::depthModeFor3D(gl::DepthMode::Mask mask) const { + return gl::DepthMode { gl::DepthMode::LessEqual, mask, { 0.0, 1.0 } }; +} + +gl::StencilMode PaintParameters::stencilModeForClipping(const ClipID& id) const { + return gl::StencilMode { + gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) }, + static_cast<int32_t>(id.reference.to_ulong()), + 0, + gl::StencilMode::Keep, + gl::StencilMode::Keep, + gl::StencilMode::Replace + }; +} + +gl::ColorMode PaintParameters::colorModeForRenderPass() const { + if (debugOptions & MapDebugOptions::Overdraw) { + const float overdraw = 1.0f / 8.0f; + return gl::ColorMode { + gl::ColorMode::Add { + gl::ColorMode::ConstantColor, + gl::ColorMode::One + }, + Color { overdraw, overdraw, overdraw, 0.0f }, + gl::ColorMode::Mask { true, true, true, true } + }; + } else if (pass == RenderPass::Translucent) { + return gl::ColorMode::alphaBlended(); + } else { + return gl::ColorMode::unblended(); + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index 213c01cfbd..4a2c2c6f12 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -1,14 +1,78 @@ #pragma once +#include <mbgl/renderer/render_pass.hpp> +#include <mbgl/renderer/render_light.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/gl/depth_mode.hpp> +#include <mbgl/gl/stencil_mode.hpp> +#include <mbgl/gl/color_mode.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/algorithm/generate_clip_ids.hpp> + +#include <array> + namespace mbgl { +class RendererBackend; +class UpdateParameters; +class RenderStaticData; +class FrameHistory; class Programs; -class View; +class TransformState; +class ImageManager; +class LineAtlas; +class UnwrappedTileID; class PaintParameters { public: + PaintParameters(gl::Context&, + float pixelRatio, + GLContextMode, + RendererBackend&, + const UpdateParameters&, + const EvaluatedLight&, + RenderStaticData&, + FrameHistory&, + ImageManager&, + LineAtlas&); + + gl::Context& context; + RendererBackend& backend; + + const TransformState& state; + const EvaluatedLight& evaluatedLight; + + RenderStaticData& staticData; + FrameHistory& frameHistory; + ImageManager& imageManager; + LineAtlas& lineAtlas; + + RenderPass pass = RenderPass::Opaque; + MapMode mapMode; + MapDebugOptions debugOptions; + GLContextMode contextMode; + TimePoint timePoint; + + float pixelRatio; + std::array<float, 2> pixelsToGLUnits; + algorithm::ClipIDGenerator clipIDGenerator; + Programs& programs; - View& view; + + gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const; + gl::DepthMode depthModeFor3D(gl::DepthMode::Mask) const; + gl::StencilMode stencilModeForClipping(const ClipID&) const; + gl::ColorMode colorModeForRenderPass() const; + + mat4 matrixForTile(const UnwrappedTileID&); + + mat4 projMatrix; + mat4 nearClippedProjMatrix; + + int numSublayers = 3; + uint32_t currentLayer; + float depthRangeSize; + const float depthEpsilon = 1.0f / (1 << 16); }; } // namespace mbgl diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp index 36d2e98082..652948c8df 100644 --- a/src/mbgl/renderer/paint_property_binder.hpp +++ b/src/mbgl/renderer/paint_property_binder.hpp @@ -5,6 +5,7 @@ #include <mbgl/gl/uniform.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/util/type_list.hpp> +#include <mbgl/renderer/possibly_evaluated_property_value.hpp> #include <mbgl/renderer/paint_property_statistics.hpp> #include <bitset> @@ -217,7 +218,11 @@ public: } float interpolationFactor(float currentZoom) const override { - return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom); + if (function.useIntegerZoom) { + return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, std::floor(currentZoom)); + } else { + return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom); + } } T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override { diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp deleted file mode 100644 index fbaf40d5c0..0000000000 --- a/src/mbgl/renderer/painter.cpp +++ /dev/null @@ -1,430 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_source.hpp> - -#include <mbgl/style/source.hpp> -#include <mbgl/style/source_impl.hpp> - -#include <mbgl/map/view.hpp> - -#include <mbgl/util/logging.hpp> -#include <mbgl/gl/debugging.hpp> - -#include <mbgl/style/style.hpp> -#include <mbgl/style/layer_impl.hpp> - -#include <mbgl/tile/tile.hpp> -#include <mbgl/renderer/render_background_layer.hpp> -#include <mbgl/renderer/render_custom_layer.hpp> -#include <mbgl/style/layers/custom_layer_impl.hpp> -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> - -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/geometry/line_atlas.hpp> -#include <mbgl/text/glyph_atlas.hpp> - -#include <mbgl/programs/program_parameters.hpp> -#include <mbgl/programs/programs.hpp> - -#include <mbgl/algorithm/generate_clip_ids.hpp> -#include <mbgl/algorithm/generate_clip_ids_impl.hpp> - -#include <mbgl/util/constants.hpp> -#include <mbgl/util/mat3.hpp> -#include <mbgl/util/string.hpp> - -#include <mbgl/util/stopwatch.hpp> - -#include <cassert> -#include <algorithm> -#include <iostream> -#include <unordered_set> - -namespace mbgl { - -using namespace style; - -static gl::VertexVector<FillLayoutVertex> tileVertices() { - gl::VertexVector<FillLayoutVertex> result; - result.emplace_back(FillProgram::layoutVertex({ 0, 0 })); - result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, 0 })); - result.emplace_back(FillProgram::layoutVertex({ 0, util::EXTENT })); - result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, util::EXTENT })); - return result; -} - -static gl::IndexVector<gl::Triangles> quadTriangleIndices() { - gl::IndexVector<gl::Triangles> result; - result.emplace_back(0, 1, 2); - result.emplace_back(1, 2, 3); - return result; -} - -static gl::IndexVector<gl::LineStrip> tileLineStripIndices() { - gl::IndexVector<gl::LineStrip> result; - result.emplace_back(0); - result.emplace_back(1); - result.emplace_back(3); - result.emplace_back(2); - result.emplace_back(0); - return result; -} - -static gl::VertexVector<RasterLayoutVertex> rasterVertices() { - gl::VertexVector<RasterLayoutVertex> result; - result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 })); - result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { 32767, 0 })); - result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, 32767 })); - result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { 32767, 32767 })); - return result; -} - -static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() { - gl::VertexVector<ExtrusionTextureLayoutVertex> result; - result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 })); - result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 })); - result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 })); - result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 })); - return result; -} - - -Painter::Painter(gl::Context& context_, - const TransformState& state_, - float pixelRatio, - const optional<std::string>& programCacheDir) - : context(context_), - state(state_), - tileVertexBuffer(context.createVertexBuffer(tileVertices())), - rasterVertexBuffer(context.createVertexBuffer(rasterVertices())), - extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())), - quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())), - tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())) { - - tileTriangleSegments.emplace_back(0, 0, 4, 6); - tileBorderSegments.emplace_back(0, 0, 4, 5); - rasterSegments.emplace_back(0, 0, 4, 6); - extrusionTextureSegments.emplace_back(0, 0, 4, 6); - - programs = std::make_unique<Programs>(context, - ProgramParameters{ pixelRatio, false, programCacheDir }); -#ifndef NDEBUG - overdrawPrograms = - std::make_unique<Programs>(context, ProgramParameters{ pixelRatio, true, programCacheDir }); -#endif -} - -Painter::~Painter() = default; - -bool Painter::needsAnimation() const { - return frameHistory.needsAnimation(util::DEFAULT_FADE_DURATION); -} - -void Painter::cleanup() { - context.performCleanup(); -} - -void Painter::render(const Style& style, const FrameData& frame_, View& view, SpriteAtlas& annotationSpriteAtlas) { - frame = frame_; - if (frame.contextMode == GLContextMode::Shared) { - context.setDirtyState(); - } - - PaintParameters parameters { -#ifndef NDEBUG - paintMode() == PaintMode::Overdraw ? *overdrawPrograms : *programs, -#else - *programs, -#endif - view - }; - - glyphAtlas = style.glyphAtlas.get(); - spriteAtlas = style.spriteAtlas.get(); - lineAtlas = style.lineAtlas.get(); - - evaluatedLight = style.getRenderLight()->getEvaluated(); - - RenderData renderData = style.getRenderData(frame.debugOptions, state.getAngle()); - const std::vector<RenderItem>& order = renderData.order; - const std::unordered_set<RenderSource*>& sources = renderData.sources; - - // Update the default matrices to the current viewport dimensions. - state.getProjMatrix(projMatrix); - // Calculate a second projection matrix with the near plane clipped to 100 so as - // not to waste lots of depth buffer precision on very close empty space, for layer - // types (fill-extrusion) that use the depth buffer to emulate real-world space. - state.getProjMatrix(nearClippedProjMatrix, 100); - - pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }}; - if (state.getViewportMode() == ViewportMode::FlippedY) { - pixelsToGLUnits[1] *= -1; - } - - frameHistory.record(frame.timePoint, state.getZoom(), - frame.mapMode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Milliseconds(0)); - - - // - UPLOAD PASS ------------------------------------------------------------------------------- - // Uploads all required buffers and images before we do any actual rendering. - { - MBGL_DEBUG_GROUP(context, "upload"); - - spriteAtlas->upload(context, 0); - - lineAtlas->upload(context, 0); - glyphAtlas->upload(context, 0); - frameHistory.upload(context, 0); - annotationSpriteAtlas.upload(context, 0); - - for (const auto& item : order) { - for (const auto& tileRef : item.tiles) { - const auto& bucket = tileRef.get().tile.getBucket(item.layer); - if (bucket && bucket->needsUpload()) { - bucket->upload(context); - } - } - } - } - - // - CLEAR ------------------------------------------------------------------------------------- - // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any - // tiles whatsoever. - { - MBGL_DEBUG_GROUP(context, "clear"); - view.bind(); - context.clear(paintMode() == PaintMode::Overdraw - ? Color::black() - : renderData.backgroundColor, - 1.0f, - 0); - } - - // - CLIPPING MASKS ---------------------------------------------------------------------------- - // Draws the clipping masks to the stencil buffer. - { - MBGL_DEBUG_GROUP(context, "clip"); - - // Update all clipping IDs. - algorithm::ClipIDGenerator generator; - for (const auto& source : sources) { - source->startRender(generator, projMatrix, nearClippedProjMatrix, state); - } - - MBGL_DEBUG_GROUP(context, "clipping masks"); - - for (const auto& stencil : generator.getStencils()) { - MBGL_DEBUG_GROUP(context, std::string{ "mask: " } + util::toString(stencil.first)); - renderClippingMask(stencil.first, stencil.second); - } - } - -#if not MBGL_USE_GLES2 and not defined(NDEBUG) - if (frame.debugOptions & MapDebugOptions::StencilClip) { - renderClipMasks(parameters); - return; - } -#endif - - // Actually render the layers - if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; } - - depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon; - - // - OPAQUE PASS ------------------------------------------------------------------------------- - // Render everything top-to-bottom by using reverse iterators. Render opaque objects first. - renderPass(parameters, - RenderPass::Opaque, - order.rbegin(), order.rend(), - 0, 1); - - // - TRANSLUCENT PASS -------------------------------------------------------------------------- - // Make a second pass, rendering translucent objects. This time, we render bottom-to-top. - renderPass(parameters, - RenderPass::Translucent, - order.begin(), order.end(), - static_cast<uint32_t>(order.size()) - 1, -1); - - if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; } - - // - DEBUG PASS -------------------------------------------------------------------------------- - // Renders debug overlays. - { - MBGL_DEBUG_GROUP(context, "debug"); - - // Finalize the rendering, e.g. by calling debug render calls per tile. - // This guarantees that we have at least one function per tile called. - // When only rendering layers via the stylesheet, it's possible that we don't - // ever visit a tile during rendering. - for (const auto& source : sources) { - source->finishRender(*this); - } - } - -#if not MBGL_USE_GLES2 and not defined(NDEBUG) - if (frame.debugOptions & MapDebugOptions::DepthBuffer) { - renderDepthBuffer(parameters); - } -#endif - - // TODO: Find a better way to unbind VAOs after we're done with them without introducing - // unnecessary bind(0)/bind(N) sequences. - { - MBGL_DEBUG_GROUP(context, "cleanup"); - - context.activeTexture = 1; - context.texture[1] = 0; - context.activeTexture = 0; - context.texture[0] = 0; - - context.bindVertexArray = 0; - } -} - -template <class Iterator> -void Painter::renderPass(PaintParameters& parameters, - RenderPass pass_, - Iterator it, Iterator end, - uint32_t i, int8_t increment) { - pass = pass_; - - MBGL_DEBUG_GROUP(context, pass == RenderPass::Opaque ? "opaque" : "translucent"); - - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", - pass == RenderPass::Opaque ? "opaque" : "translucent"); - } - - for (; it != end; ++it, i += increment) { - currentLayer = i; - - const auto& item = *it; - const RenderLayer& layer = item.layer; - - if (!layer.hasRenderPass(pass)) - continue; - - if (layer.is<RenderBackgroundLayer>()) { - MBGL_DEBUG_GROUP(context, "background"); - renderBackground(parameters, *layer.as<RenderBackgroundLayer>()); - } else if (layer.is<RenderCustomLayer>()) { - MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - custom"); - - // Reset GL state to a known state so the CustomLayer always has a clean slate. - context.bindVertexArray = 0; - context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadOnly)); - context.setStencilMode(gl::StencilMode::disabled()); - context.setColorMode(colorModeForRenderPass()); - - layer.as<RenderCustomLayer>()->impl->render(state); - - // Reset the view back to our original one, just in case the CustomLayer changed - // the viewport or Framebuffer. - parameters.view.bind(); - context.setDirtyState(); - } else if (layer.is<RenderFillExtrusionLayer>()) { - const auto size = context.viewport.getCurrentValue().size; - - if (!extrusionTexture || extrusionTexture->getSize() != size) { - extrusionTexture = OffscreenTexture(context, size, OffscreenTextureAttachment::Depth); - } - - extrusionTexture->bind(); - - context.setStencilMode(gl::StencilMode::disabled()); - context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadWrite)); - context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {}); - - for (auto& tileRef : item.tiles) { - auto& tile = tileRef.get(); - - MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id)); - auto bucket = tile.tile.getBucket(layer); - bucket->render(*this, parameters, layer, tile); - } - - parameters.view.bind(); - context.bindTexture(extrusionTexture->getTexture()); - - mat4 viewportMat; - matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); - - const PaintProperties<>::Evaluated properties{}; - - parameters.programs.extrusionTexture.draw( - context, - gl::Triangles(), - gl::DepthMode::disabled(), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - ExtrusionTextureProgram::UniformValues{ - uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, - uniforms::u_image::Value{ 0 }, - uniforms::u_opacity::Value{ layer.as<RenderFillExtrusionLayer>() - ->evaluated.get<FillExtrusionOpacity>() } }, - extrusionTextureVertexBuffer, - quadTriangleIndexBuffer, - extrusionTextureSegments, - ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, - properties, - state.getZoom(), - layer.getID()); - } else { - for (auto& tileRef : item.tiles) { - auto& tile = tileRef.get(); - MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id)); - auto bucket = tile.tile.getBucket(layer); - bucket->render(*this, parameters, layer, tile); - } - } - } - - if (debug::renderTree) { - Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}"); - } -} - -mat4 Painter::matrixForTile(const UnwrappedTileID& tileID) { - mat4 matrix; - state.matrixFor(matrix, tileID); - matrix::multiply(matrix, projMatrix, matrix); - return matrix; -} - -gl::DepthMode Painter::depthModeForSublayer(uint8_t n, gl::DepthMode::Mask mask) const { - float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon; - float farDepth = nearDepth + depthRangeSize; - return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } }; -} - -gl::StencilMode Painter::stencilModeForClipping(const ClipID& id) const { - return gl::StencilMode { - gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) }, - static_cast<int32_t>(id.reference.to_ulong()), - 0, - gl::StencilMode::Keep, - gl::StencilMode::Keep, - gl::StencilMode::Replace - }; -} - -gl::ColorMode Painter::colorModeForRenderPass() const { - if (paintMode() == PaintMode::Overdraw) { - const float overdraw = 1.0f / 8.0f; - return gl::ColorMode { - gl::ColorMode::Add { - gl::ColorMode::ConstantColor, - gl::ColorMode::One - }, - Color { overdraw, overdraw, overdraw, 0.0f }, - gl::ColorMode::Mask { true, true, true, true } - }; - } else if (pass == RenderPass::Translucent) { - return gl::ColorMode::alphaBlended(); - } else { - return gl::ColorMode::unblended(); - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp deleted file mode 100644 index 1919ad924e..0000000000 --- a/src/mbgl/renderer/painter.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include <mbgl/map/transform_state.hpp> - -#include <mbgl/tile/tile_id.hpp> - -#include <mbgl/renderer/frame_history.hpp> -#include <mbgl/renderer/render_item.hpp> -#include <mbgl/renderer/bucket.hpp> - -#include <mbgl/gl/context.hpp> -#include <mbgl/programs/debug_program.hpp> -#include <mbgl/programs/program_parameters.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/programs/extrusion_texture_program.hpp> -#include <mbgl/programs/raster_program.hpp> - -#include <mbgl/style/style.hpp> - -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/chrono.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/offscreen_texture.hpp> - -#include <array> -#include <vector> -#include <set> -#include <map> - -namespace mbgl { - -class RenderTile; -class SpriteAtlas; -class View; -class GlyphAtlas; -class LineAtlas; -struct FrameData; -class Tile; - -class DebugBucket; -class FillBucket; -class FillExtrusionBucket; -class LineBucket; -class CircleBucket; -class SymbolBucket; -class RasterBucket; - -class RenderFillLayer; -class RenderFillExtrusionLayer; -class RenderLineLayer; -class RenderCircleLayer; -class RenderSymbolLayer; -class RenderRasterLayer; -class RenderBackgroundLayer; - -class Programs; -class PaintParameters; -class TilePyramid; - -struct ClipID; - -namespace style { -class Style; -class Source; -} // namespace style - -struct FrameData { - TimePoint timePoint; - float pixelRatio; - MapMode mapMode; - GLContextMode contextMode; - MapDebugOptions debugOptions; -}; - -class Painter : private util::noncopyable { -public: - Painter(gl::Context&, const TransformState&, float pixelRatio, const optional<std::string>& programCacheDir); - ~Painter(); - - void render(const style::Style&, - const FrameData&, - View&, - SpriteAtlas& annotationSpriteAtlas); - - void cleanup(); - - void renderClippingMask(const UnwrappedTileID&, const ClipID&); - void renderTileDebug(const RenderTile&); - void renderFill(PaintParameters&, FillBucket&, const RenderFillLayer&, const RenderTile&); - void renderFillExtrusion(PaintParameters&, FillExtrusionBucket&, const RenderFillExtrusionLayer&, const RenderTile&); - void renderLine(PaintParameters&, LineBucket&, const RenderLineLayer&, const RenderTile&); - void renderCircle(PaintParameters&, CircleBucket&, const RenderCircleLayer&, const RenderTile&); - void renderSymbol(PaintParameters&, SymbolBucket&, const RenderSymbolLayer&, const RenderTile&); - void renderRaster(PaintParameters&, RasterBucket&, const RenderRasterLayer&, const RenderTile&); - void renderBackground(PaintParameters&, const RenderBackgroundLayer&); - -#ifndef NDEBUG - // Renders tile clip boundaries, using stencil buffer to calculate fill color. - void renderClipMasks(PaintParameters&); - // Renders the depth buffer. - void renderDepthBuffer(PaintParameters&); -#endif - - bool needsAnimation() const; - -private: - std::vector<RenderItem> determineRenderOrder(const style::Style&); - - template <class Iterator> - void renderPass(PaintParameters&, - RenderPass, - Iterator it, Iterator end, - uint32_t i, int8_t increment); - - mat4 matrixForTile(const UnwrappedTileID&); - gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const; - gl::StencilMode stencilModeForClipping(const ClipID&) const; - gl::ColorMode colorModeForRenderPass() const; - -#ifndef NDEBUG - PaintMode paintMode() const { - return frame.debugOptions & MapDebugOptions::Overdraw ? PaintMode::Overdraw - : PaintMode::Regular; - } -#else - PaintMode paintMode() const { - return PaintMode::Regular; - } -#endif - -private: - gl::Context& context; - - mat4 projMatrix; - mat4 nearClippedProjMatrix; - - std::array<float, 2> pixelsToGLUnits; - - const mat4 identityMatrix = []{ - mat4 identity; - matrix::identity(identity); - return identity; - }(); - - const TransformState& state; - - FrameData frame; - - int indent = 0; - - RenderPass pass = RenderPass::Opaque; - - int numSublayers = 3; - uint32_t currentLayer; - float depthRangeSize; - const float depthEpsilon = 1.0f / (1 << 16); - - SpriteAtlas* spriteAtlas = nullptr; - GlyphAtlas* glyphAtlas = nullptr; - LineAtlas* lineAtlas = nullptr; - - optional<OffscreenTexture> extrusionTexture; - - EvaluatedLight evaluatedLight; - - FrameHistory frameHistory; - - std::unique_ptr<Programs> programs; -#ifndef NDEBUG - std::unique_ptr<Programs> overdrawPrograms; -#endif - - gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer; - gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer; - gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer; - - gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer; - gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer; - - SegmentVector<FillAttributes> tileTriangleSegments; - SegmentVector<DebugAttributes> tileBorderSegments; - SegmentVector<RasterAttributes> rasterSegments; - SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp deleted file mode 100644 index 9bd9431082..0000000000 --- a/src/mbgl/renderer/painter_background.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/render_background_layer.hpp> -#include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/util/tile_cover.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderBackground(PaintParameters& parameters, const RenderBackgroundLayer& layer) { - // Note that for bottommost layers without a pattern, the background color is drawn with - // glClear rather than this method. - const BackgroundPaintProperties::Evaluated& background = layer.evaluated; - - style::FillPaintProperties::Evaluated properties; - properties.get<FillPattern>() = background.get<BackgroundPattern>(); - properties.get<FillOpacity>() = { background.get<BackgroundOpacity>() }; - properties.get<FillColor>() = { background.get<BackgroundColor>() }; - - const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); - - if (!background.get<BackgroundPattern>().to.empty()) { - optional<SpriteAtlasElement> imagePosA = spriteAtlas->getPattern(background.get<BackgroundPattern>().from); - optional<SpriteAtlasElement> imagePosB = spriteAtlas->getPattern(background.get<BackgroundPattern>().to); - - if (!imagePosA || !imagePosB) - return; - - spriteAtlas->bind(true, context, 0); - - for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) { - parameters.programs.fillPattern.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - FillPatternUniforms::values( - matrixForTile(tileID), - context.viewport.getCurrentValue().size, - *imagePosA, - *imagePosB, - background.get<BackgroundPattern>(), - tileID, - state - ), - tileVertexBuffer, - quadTriangleIndexBuffer, - tileTriangleSegments, - paintAttibuteData, - properties, - state.getZoom(), - layer.getID() - ); - } - } else { - for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) { - parameters.programs.fill.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - FillProgram::UniformValues { - uniforms::u_matrix::Value{ matrixForTile(tileID) }, - uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, - }, - tileVertexBuffer, - quadTriangleIndexBuffer, - tileTriangleSegments, - paintAttibuteData, - properties, - state.getZoom(), - layer.getID() - ); - } - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_circle.cpp b/src/mbgl/renderer/painter_circle.cpp deleted file mode 100644 index ecd7598de9..0000000000 --- a/src/mbgl/renderer/painter_circle.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/circle_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_circle_layer.hpp> -#include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/circle_program.hpp> -#include <mbgl/gl/context.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderCircle(PaintParameters& parameters, - CircleBucket& bucket, - const RenderCircleLayer& layer, - const RenderTile& tile) { - if (pass == RenderPass::Opaque) { - return; - } - - const CirclePaintProperties::Evaluated& properties = layer.evaluated; - const bool scaleWithMap = properties.get<CirclePitchScale>() == CirclePitchScaleType::Map; - - parameters.programs.circle.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - frame.mapMode == MapMode::Still - ? stencilModeForClipping(tile.clip) - : gl::StencilMode::disabled(), - colorModeForRenderPass(), - CircleProgram::UniformValues { - uniforms::u_matrix::Value{ - tile.translatedMatrix(properties.get<CircleTranslate>(), - properties.get<CircleTranslateAnchor>(), - state) - }, - uniforms::u_scale_with_map::Value{ scaleWithMap }, - uniforms::u_extrude_scale::Value{ scaleWithMap - ? std::array<float, 2> {{ - pixelsToGLUnits[0] * state.getCameraToCenterDistance(), - pixelsToGLUnits[1] * state.getCameraToCenterDistance() - }} - : pixelsToGLUnits } - }, - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID() - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp deleted file mode 100644 index 162f3b1d96..0000000000 --- a/src/mbgl/renderer/painter_clipping.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/util/clip_id.hpp> - -namespace mbgl { - -void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& clip) { - static const style::FillPaintProperties::Evaluated properties {}; - static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); - programs->fill.get(properties).draw( - context, - gl::Triangles(), - gl::DepthMode::disabled(), - gl::StencilMode { - gl::StencilMode::Always(), - static_cast<int32_t>(clip.reference.to_ulong()), - 0b11111111, - gl::StencilMode::Keep, - gl::StencilMode::Keep, - gl::StencilMode::Replace - }, - gl::ColorMode::disabled(), - FillProgram::UniformValues { - uniforms::u_matrix::Value{ matrixForTile(tileID) }, - uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, - }, - tileVertexBuffer, - quadTriangleIndexBuffer, - tileTriangleSegments, - paintAttibuteData, - properties, - state.getZoom(), - "clipping" - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp deleted file mode 100644 index 9a24ab5422..0000000000 --- a/src/mbgl/renderer/painter_debug.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/debug_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/map/view.hpp> -#include <mbgl/tile/tile.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/util/string.hpp> -#include <mbgl/gl/debugging.hpp> -#include <mbgl/util/color.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderTileDebug(const RenderTile& renderTile) { - if (frame.debugOptions == MapDebugOptions::NoDebug) - return; - - MBGL_DEBUG_GROUP(context, std::string { "debug " } + util::toString(renderTile.id)); - - static const style::PaintProperties<>::Evaluated properties {}; - static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); - - auto draw = [&] (Color color, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments, auto drawMode) { - programs->debug.draw( - context, - drawMode, - gl::DepthMode::disabled(), - stencilModeForClipping(renderTile.clip), - gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ renderTile.matrix }, - uniforms::u_color::Value{ color } - }, - vertexBuffer, - indexBuffer, - segments, - paintAttibuteData, - properties, - state.getZoom(), - "debug" - ); - }; - - if (frame.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) { - Tile& tile = renderTile.tile; - if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() || - tile.debugBucket->complete != tile.isComplete() || - !(tile.debugBucket->modified == tile.modified) || - !(tile.debugBucket->expires == tile.expires) || - tile.debugBucket->debugMode != frame.debugOptions) { - tile.debugBucket = std::make_unique<DebugBucket>( - tile.id, tile.isRenderable(), tile.isComplete(), tile.modified, - tile.expires, frame.debugOptions, context); - } - - draw(Color::white(), - *tile.debugBucket->vertexBuffer, - *tile.debugBucket->indexBuffer, - tile.debugBucket->segments, - gl::Lines { 4.0f * frame.pixelRatio }); - - draw(Color::black(), - *tile.debugBucket->vertexBuffer, - *tile.debugBucket->indexBuffer, - tile.debugBucket->segments, - gl::Lines { 2.0f * frame.pixelRatio }); - } - - if (frame.debugOptions & MapDebugOptions::TileBorders) { - draw(Color::red(), - tileVertexBuffer, - tileBorderIndexBuffer, - tileBorderSegments, - gl::LineStrip { 4.0f * frame.pixelRatio }); - } -} - -#ifndef NDEBUG -void Painter::renderClipMasks(PaintParameters&) { - context.setStencilMode(gl::StencilMode::disabled()); - context.setDepthMode(gl::DepthMode::disabled()); - context.setColorMode(gl::ColorMode::unblended()); - context.program = 0; - -#if not MBGL_USE_GLES2 - // Reset the value in case someone else changed it, or it's dirty. - context.pixelTransferStencil = gl::value::PixelTransferStencil::Default; - - // Read the stencil buffer - const auto viewport = context.viewport.getCurrentValue(); - auto image = - context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false); - - // Scale the Stencil buffer to cover the entire color space. - auto it = image.data.get(); - auto end = it + viewport.size.width * viewport.size.height; - const auto factor = 255.0f / *std::max_element(it, end); - for (; it != end; ++it) { - *it *= factor; - } - - context.pixelZoom = { 1, 1 }; - context.rasterPos = { -1, -1, 0, 1 }; - context.drawPixels(image); -#endif // MBGL_USE_GLES2 -} - -void Painter::renderDepthBuffer(PaintParameters&) { - context.setStencilMode(gl::StencilMode::disabled()); - context.setDepthMode(gl::DepthMode::disabled()); - context.setColorMode(gl::ColorMode::unblended()); - context.program = 0; - -#if not MBGL_USE_GLES2 - // Scales the values in the depth buffer so that they cover the entire grayscale range. This - // makes it easier to spot tiny differences. - const float base = 1.0f / (1.0f - depthRangeSize); - context.pixelTransferDepth = { base, 1.0f - base }; - - // Read the stencil buffer - auto viewport = context.viewport.getCurrentValue(); - auto image = - context.readFramebuffer<AlphaImage, gl::TextureFormat::Depth>(viewport.size, false); - - context.pixelZoom = { 1, 1 }; - context.rasterPos = { -1, -1, 0, 1 }; - context.drawPixels(image); -#endif // MBGL_USE_GLES2 -} -#endif // NDEBUG - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp deleted file mode 100644 index 7264735692..0000000000 --- a/src/mbgl/renderer/painter_fill.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/fill_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_fill_layer.hpp> -#include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_program.hpp> -#include <mbgl/util/convert.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderFill(PaintParameters& parameters, - FillBucket& bucket, - const RenderFillLayer& layer, - const RenderTile& tile) { - const FillPaintProperties::Evaluated& properties = layer.evaluated; - - if (!properties.get<FillPattern>().from.empty()) { - if (pass != RenderPass::Translucent) { - return; - } - - optional<SpriteAtlasElement> imagePosA = spriteAtlas->getPattern(properties.get<FillPattern>().from); - optional<SpriteAtlasElement> imagePosB = spriteAtlas->getPattern(properties.get<FillPattern>().to); - - if (!imagePosA || !imagePosB) { - return; - } - - spriteAtlas->bind(true, context, 0); - - auto draw = [&] (uint8_t sublayer, - auto& program, - const auto& drawMode, - const auto& indexBuffer, - const auto& segments) { - program.get(properties).draw( - context, - drawMode, - depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite), - stencilModeForClipping(tile.clip), - colorModeForRenderPass(), - FillPatternUniforms::values( - tile.translatedMatrix(properties.get<FillTranslate>(), - properties.get<FillTranslateAnchor>(), - state), - context.viewport.getCurrentValue().size, - *imagePosA, - *imagePosB, - properties.get<FillPattern>(), - tile.id, - state - ), - *bucket.vertexBuffer, - indexBuffer, - segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID() - ); - }; - - draw(0, - parameters.programs.fillPattern, - gl::Triangles(), - *bucket.triangleIndexBuffer, - bucket.triangleSegments); - - if (!properties.get<FillAntialias>() || !layer.unevaluated.get<FillOutlineColor>().isUndefined()) { - return; - } - - draw(2, - parameters.programs.fillOutlinePattern, - gl::Lines { 2.0f }, - *bucket.lineIndexBuffer, - bucket.lineSegments); - } else { - auto draw = [&] (uint8_t sublayer, - auto& program, - const auto& drawMode, - const auto& indexBuffer, - const auto& segments) { - program.get(properties).draw( - context, - drawMode, - depthModeForSublayer(sublayer, pass == RenderPass::Opaque - ? gl::DepthMode::ReadWrite - : gl::DepthMode::ReadOnly), - stencilModeForClipping(tile.clip), - colorModeForRenderPass(), - FillProgram::UniformValues { - uniforms::u_matrix::Value{ - tile.translatedMatrix(properties.get<FillTranslate>(), - properties.get<FillTranslateAnchor>(), - state) - }, - uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, - }, - *bucket.vertexBuffer, - indexBuffer, - segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID() - ); - }; - - if (properties.get<FillAntialias>() && !layer.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) { - draw(2, - parameters.programs.fillOutline, - gl::Lines { 2.0f }, - *bucket.lineIndexBuffer, - bucket.lineSegments); - } - - // Only draw the fill when it's opaque and we're drawing opaque fragments, - // or when it's translucent and we're drawing translucent fragments. - if ((properties.get<FillColor>().constantOr(Color()).a >= 1.0f - && properties.get<FillOpacity>().constantOr(0) >= 1.0f) == (pass == RenderPass::Opaque)) { - draw(1, - parameters.programs.fill, - gl::Triangles(), - *bucket.triangleIndexBuffer, - bucket.triangleSegments); - } - - if (properties.get<FillAntialias>() && layer.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) { - draw(2, - parameters.programs.fillOutline, - gl::Lines { 2.0f }, - *bucket.lineIndexBuffer, - bucket.lineSegments); - } - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_fill_extrusion.cpp b/src/mbgl/renderer/painter_fill_extrusion.cpp deleted file mode 100644 index 5581cfe983..0000000000 --- a/src/mbgl/renderer/painter_fill_extrusion.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/fill_extrusion_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> -#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/fill_extrusion_program.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/convert.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderFillExtrusion(PaintParameters& parameters, - FillExtrusionBucket& bucket, - const RenderFillExtrusionLayer& layer, - const RenderTile& tile) { - const FillExtrusionPaintProperties::Evaluated& properties = layer.evaluated; - - if (pass == RenderPass::Opaque) { - return; - } - - if (!properties.get<FillExtrusionPattern>().from.empty()) { - optional<SpriteAtlasElement> imagePosA = - spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().from); - optional<SpriteAtlasElement> imagePosB = - spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().to); - - if (!imagePosA || !imagePosB) { - return; - } - - spriteAtlas->bind(true, context, 0); - - parameters.programs.fillExtrusionPattern.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - FillExtrusionPatternUniforms::values( - tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(), - properties.get<FillExtrusionTranslateAnchor>(), - state), - *imagePosA, - *imagePosB, - properties.get<FillExtrusionPattern>(), - tile.id, - state, - -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, - evaluatedLight - ), - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.triangleSegments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID()); - - } else { - parameters.programs.fillExtrusion.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - FillExtrusionUniforms::values( - tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(), - properties.get<FillExtrusionTranslateAnchor>(), - state), - state, - evaluatedLight - ), - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.triangleSegments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID()); - }; -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp deleted file mode 100644 index 209b1447d0..0000000000 --- a/src/mbgl/renderer/painter_line.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_line_layer.hpp> -#include <mbgl/style/layers/line_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/line_program.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/geometry/line_atlas.hpp> - -namespace mbgl { - -using namespace style; - -void Painter::renderLine(PaintParameters& parameters, - LineBucket& bucket, - const RenderLineLayer& layer, - const RenderTile& tile) { - if (pass == RenderPass::Opaque) { - return; - } - - const LinePaintProperties::Evaluated& properties = layer.evaluated; - - auto draw = [&] (auto& program, auto&& uniformValues) { - program.get(properties).draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - stencilModeForClipping(tile.clip), - colorModeForRenderPass(), - std::move(uniformValues), - *bucket.vertexBuffer, - *bucket.indexBuffer, - bucket.segments, - bucket.paintPropertyBinders.at(layer.getID()), - properties, - state.getZoom(), - layer.getID() - ); - }; - - if (!properties.get<LineDasharray>().from.empty()) { - const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round - ? LinePatternCap::Round : LinePatternCap::Square; - LinePatternPos posA = lineAtlas->getDashPosition(properties.get<LineDasharray>().from, cap); - LinePatternPos posB = lineAtlas->getDashPosition(properties.get<LineDasharray>().to, cap); - - lineAtlas->bind(context, 0); - - draw(parameters.programs.lineSDF, - LineSDFProgram::uniformValues( - properties, - frame.pixelRatio, - tile, - state, - pixelsToGLUnits, - posA, - posB, - layer.dashLineWidth, - lineAtlas->getSize().width)); - - } else if (!properties.get<LinePattern>().from.empty()) { - optional<SpriteAtlasElement> posA = spriteAtlas->getPattern(properties.get<LinePattern>().from); - optional<SpriteAtlasElement> posB = spriteAtlas->getPattern(properties.get<LinePattern>().to); - - if (!posA || !posB) - return; - - spriteAtlas->bind(true, context, 0); - - draw(parameters.programs.linePattern, - LinePatternProgram::uniformValues( - properties, - tile, - state, - pixelsToGLUnits, - *posA, - *posB)); - - } else { - draw(parameters.programs.line, - LineProgram::uniformValues( - properties, - tile, - state, - pixelsToGLUnits)); - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp deleted file mode 100644 index f0e5399f4a..0000000000 --- a/src/mbgl/renderer/painter_raster.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/raster_bucket.hpp> -#include <mbgl/renderer/render_raster_layer.hpp> -#include <mbgl/style/layers/raster_layer_impl.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/raster_program.hpp> - -namespace mbgl { - -using namespace style; - -static float saturationFactor(float saturation) { - if (saturation > 0) { - return 1 - 1 / (1.001 - saturation); - } else { - return -saturation; - } -} - -static float contrastFactor(float contrast) { - if (contrast > 0) { - return 1 / (1 - contrast); - } else { - return 1 + contrast; - } -} - -static std::array<float, 3> spinWeights(float spin) { - spin *= util::DEG2RAD; - float s = std::sin(spin); - float c = std::cos(spin); - std::array<float, 3> spin_weights = {{ - (2 * c + 1) / 3, - (-std::sqrt(3.0f) * s - c + 1) / 3, - (std::sqrt(3.0f) * s - c + 1) / 3 - }}; - return spin_weights; -} - -void Painter::renderRaster(PaintParameters& parameters, - RasterBucket& bucket, - const RenderRasterLayer& layer, - const RenderTile& tile) { - if (pass != RenderPass::Translucent) - return; - if (!bucket.hasData()) - return; - - const RasterPaintProperties::Evaluated& properties = layer.evaluated; - const RasterProgram::PaintPropertyBinders paintAttributeData(properties, 0); - - assert(bucket.texture); - context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); - context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); - - parameters.programs.raster.draw( - context, - gl::Triangles(), - depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - RasterProgram::UniformValues { - uniforms::u_matrix::Value{ tile.matrix }, - uniforms::u_image0::Value{ 0 }, - uniforms::u_image1::Value{ 1 }, - uniforms::u_opacity::Value{ properties.get<RasterOpacity>() }, - uniforms::u_fade_t::Value{ 1 }, - uniforms::u_brightness_low::Value{ properties.get<RasterBrightnessMin>() }, - uniforms::u_brightness_high::Value{ properties.get<RasterBrightnessMax>() }, - uniforms::u_saturation_factor::Value{ saturationFactor(properties.get<RasterSaturation>()) }, - uniforms::u_contrast_factor::Value{ contrastFactor(properties.get<RasterContrast>()) }, - uniforms::u_spin_weights::Value{ spinWeights(properties.get<RasterHueRotate>()) }, - uniforms::u_buffer_scale::Value{ 1.0f }, - uniforms::u_scale_parent::Value{ 1.0f }, - uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} }, - }, - rasterVertexBuffer, - quadTriangleIndexBuffer, - rasterSegments, - paintAttributeData, - properties, - state.getZoom(), - layer.getID() - ); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp deleted file mode 100644 index 6462eebdcc..0000000000 --- a/src/mbgl/renderer/painter_symbol.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/paint_parameters.hpp> -#include <mbgl/renderer/symbol_bucket.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> -#include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/text/glyph_atlas.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/programs/programs.hpp> -#include <mbgl/programs/symbol_program.hpp> -#include <mbgl/programs/collision_box_program.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/tile/tile.hpp> - -#include <cmath> - -namespace mbgl { - -using namespace style; - -void Painter::renderSymbol(PaintParameters& parameters, - SymbolBucket& bucket, - const RenderSymbolLayer& layer, - const RenderTile& tile) { - if (pass == RenderPass::Opaque) { - return; - } - - const auto& layout = bucket.layout; - - frameHistory.bind(context, 1); - - auto draw = [&] (auto& program, - auto&& uniformValues, - const auto& buffers, - const auto& symbolSizeBinder, - const SymbolPropertyValues& values_, - const auto& binders, - const auto& paintProperties) - { - // We clip symbols to their tile extent in still mode. - const bool needsClipping = frame.mapMode == MapMode::Still; - - program.get(paintProperties).draw( - context, - gl::Triangles(), - values_.pitchAlignment == AlignmentType::Map - ? depthModeForSublayer(0, gl::DepthMode::ReadOnly) - : gl::DepthMode::disabled(), - needsClipping - ? stencilModeForClipping(tile.clip) - : gl::StencilMode::disabled(), - colorModeForRenderPass(), - std::move(uniformValues), - *buffers.vertexBuffer, - *symbolSizeBinder, - *buffers.indexBuffer, - buffers.segments, - binders, - paintProperties, - state.getZoom(), - layer.getID() - ); - }; - - if (bucket.hasIconData()) { - auto values = layer.iconPropertyValues(layout); - auto paintPropertyValues = layer.iconPaintProperties(); - - SpriteAtlas& atlas = *bucket.spriteAtlas; - const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || - frame.pixelRatio != atlas.getPixelRatio() || - bucket.iconsNeedLinear; - const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0; - atlas.bind(bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed, context, 0); - - const Size texsize = atlas.getSize(); - - if (bucket.sdfIcons) { - if (values.hasHalo) { - draw(parameters.programs.symbolIconSDF, - SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), - bucket.icon, - bucket.iconSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).first, - paintPropertyValues); - } - - if (values.hasFill) { - draw(parameters.programs.symbolIconSDF, - SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), - bucket.icon, - bucket.iconSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).first, - paintPropertyValues); - } - } else { - draw(parameters.programs.symbolIcon, - SymbolIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state), - bucket.icon, - bucket.iconSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).first, - paintPropertyValues); - } - } - - if (bucket.hasTextData()) { - glyphAtlas->bind(context, 0); - - auto values = layer.textPropertyValues(layout); - auto paintPropertyValues = layer.textPaintProperties(); - - const Size texsize = glyphAtlas->getSize(); - - if (values.hasHalo) { - draw(parameters.programs.symbolGlyph, - SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), - bucket.text, - bucket.textSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).second, - paintPropertyValues); - } - - if (values.hasFill) { - draw(parameters.programs.symbolGlyph, - SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), - bucket.text, - bucket.textSizeBinder, - values, - bucket.paintPropertyBinders.at(layer.getID()).second, - paintPropertyValues); - } - } - - if (bucket.hasCollisionBoxData()) { - static const style::PaintProperties<>::Evaluated properties {}; - static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0); - - programs->collisionBox.draw( - context, - gl::Lines { 1.0f }, - gl::DepthMode::disabled(), - gl::StencilMode::disabled(), - colorModeForRenderPass(), - CollisionBoxProgram::UniformValues { - uniforms::u_matrix::Value{ tile.matrix }, - uniforms::u_scale::Value{ std::pow(2.0f, float(state.getZoom() - tile.tile.id.overscaledZ)) }, - uniforms::u_zoom::Value{ float(state.getZoom() * 10) }, - uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) }, - }, - *bucket.collisionBox.vertexBuffer, - *bucket.collisionBox.indexBuffer, - bucket.collisionBox.segments, - paintAttributeData, - properties, - state.getZoom(), - layer.getID() - ); - } -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/possibly_evaluated_property_value.hpp b/src/mbgl/renderer/possibly_evaluated_property_value.hpp index a0bcec2bf1..e662d5dfb1 100644 --- a/src/mbgl/renderer/possibly_evaluated_property_value.hpp +++ b/src/mbgl/renderer/possibly_evaluated_property_value.hpp @@ -19,7 +19,9 @@ private: public: PossiblyEvaluatedPropertyValue() = default; - PossiblyEvaluatedPropertyValue(Value v) : value(std::move(v)) {} + PossiblyEvaluatedPropertyValue(Value v, bool useIntegerZoom_ = false) + : value(std::move(v)), + useIntegerZoom(useIntegerZoom_) {} bool isConstant() const { return value.template is<T>(); @@ -43,15 +45,21 @@ public: template <class Feature> T evaluate(const Feature& feature, float zoom, T defaultValue) const { return this->match( - [&] (const T& constant) { return constant; }, + [&] (const T& constant_) { return constant_; }, [&] (const style::SourceFunction<T>& function) { return function.evaluate(feature, defaultValue); }, [&] (const style::CompositeFunction<T>& function) { - return function.evaluate(zoom, feature, defaultValue); + if (useIntegerZoom) { + return function.evaluate(floor(zoom), feature, defaultValue); + } else { + return function.evaluate(zoom, feature, defaultValue); + } } ); } + + bool useIntegerZoom; }; namespace util { diff --git a/src/mbgl/renderer/property_evaluation_parameters.hpp b/src/mbgl/renderer/property_evaluation_parameters.hpp index 39b663bdb9..da6a4a0892 100644 --- a/src/mbgl/renderer/property_evaluation_parameters.hpp +++ b/src/mbgl/renderer/property_evaluation_parameters.hpp @@ -11,20 +11,24 @@ public: : z(z_), now(Clock::time_point::max()), zoomHistory(), - defaultFadeDuration(0) {} + defaultFadeDuration(0), + useIntegerZoom(false) {} PropertyEvaluationParameters(ZoomHistory zoomHistory_, TimePoint now_, - Duration defaultFadeDuration_) + Duration defaultFadeDuration_, + bool useIntegerZoom_ = false) : z(zoomHistory_.lastZoom), now(std::move(now_)), zoomHistory(std::move(zoomHistory_)), - defaultFadeDuration(std::move(defaultFadeDuration_)) {} + defaultFadeDuration(std::move(defaultFadeDuration_)), + useIntegerZoom(useIntegerZoom_) {} float z; TimePoint now; ZoomHistory zoomHistory; Duration defaultFadeDuration; + bool useIntegerZoom; }; } // namespace mbgl diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp deleted file mode 100644 index ee8ef24071..0000000000 --- a/src/mbgl/renderer/raster_bucket.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include <mbgl/renderer/raster_bucket.hpp> -#include <mbgl/renderer/render_raster_layer.hpp> -#include <mbgl/programs/raster_program.hpp> -#include <mbgl/renderer/painter.hpp> -#include <mbgl/gl/context.hpp> - -namespace mbgl { - -using namespace style; - -RasterBucket::RasterBucket(UnassociatedImage&& image_) : image(std::move(image_)) { -} - -void RasterBucket::upload(gl::Context& context) { - texture = context.createTexture(std::move(image)); - uploaded = true; -} - -void RasterBucket::render(Painter& painter, - PaintParameters& parameters, - const RenderLayer& layer, - const RenderTile& tile) { - painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), tile); -} - -bool RasterBucket::hasData() const { - return true; -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp deleted file mode 100644 index 334954e3f4..0000000000 --- a/src/mbgl/renderer/raster_bucket.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include <mbgl/renderer/bucket.hpp> -#include <mbgl/util/image.hpp> -#include <mbgl/util/optional.hpp> -#include <mbgl/gl/texture.hpp> - -namespace mbgl { - -class RasterBucket : public Bucket { -public: - RasterBucket(UnassociatedImage&&); - - void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; - bool hasData() const override; - - UnassociatedImage image; - optional<gl::Texture> texture; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_background_layer.cpp b/src/mbgl/renderer/render_background_layer.cpp deleted file mode 100644 index 485d4b16c6..0000000000 --- a/src/mbgl/renderer/render_background_layer.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include <mbgl/renderer/render_background_layer.hpp> -#include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/renderer/bucket.hpp> - -namespace mbgl { - -RenderBackgroundLayer::RenderBackgroundLayer(const style::BackgroundLayer::Impl& _impl) - : RenderLayer(style::LayerType::Background, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderBackgroundLayer::clone() const { - return std::make_unique<RenderBackgroundLayer>(*this); -} - -std::unique_ptr<Bucket> RenderBackgroundLayer::createBucket(const BucketParameters &, - const std::vector<const RenderLayer *> &) const { - assert(false); - return nullptr; -} - -void RenderBackgroundLayer::cascade(const CascadeParameters ¶meters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters ¶meters) { - evaluated = unevaluated.evaluate(parameters); - - passes = evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent - : RenderPass::None; -} - -bool RenderBackgroundLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -} diff --git a/src/mbgl/renderer/render_circle_layer.cpp b/src/mbgl/renderer/render_circle_layer.cpp deleted file mode 100644 index f59c174dd3..0000000000 --- a/src/mbgl/renderer/render_circle_layer.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include <mbgl/renderer/render_circle_layer.hpp> -#include <mbgl/renderer/circle_bucket.hpp> -#include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/intersection_tests.hpp> - -namespace mbgl { - -RenderCircleLayer::RenderCircleLayer(const style::CircleLayer::Impl& _impl) - : RenderLayer(style::LayerType::Circle, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderCircleLayer::clone() const { - return std::make_unique<RenderCircleLayer>(*this); -} - -std::unique_ptr<Bucket> RenderCircleLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<CircleBucket>(parameters, layers); -} - -void RenderCircleLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 || - evaluated.get<style::CircleStrokeWidth>().constantOr(1) > 0) - && (evaluated.get<style::CircleColor>().constantOr(Color::black()).a > 0 || - evaluated.get<style::CircleStrokeColor>().constantOr(Color::black()).a > 0) - && (evaluated.get<style::CircleOpacity>().constantOr(1) > 0 || - evaluated.get<style::CircleStrokeOpacity>().constantOr(1) > 0)) - ? RenderPass::Translucent : RenderPass::None; -} - -bool RenderCircleLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -bool RenderCircleLayer::queryIntersectsFeature( - const GeometryCoordinates& queryGeometry, - const GeometryTileFeature& feature, - const float zoom, - const float bearing, - const float pixelsToTileUnits) const { - - // Translate query geometry - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( - queryGeometry, - evaluated.get<style::CircleTranslate>(), - evaluated.get<style::CircleTranslateAnchor>(), - bearing, - pixelsToTileUnits); - - // Evaluate function - auto circleRadius = evaluated.get<style::CircleRadius>() - .evaluate(feature, zoom, style::CircleRadius::defaultValue()) - * pixelsToTileUnits; - - // Test intersection - return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius); -} - -} diff --git a/src/mbgl/renderer/render_custom_layer.cpp b/src/mbgl/renderer/render_custom_layer.cpp deleted file mode 100644 index 66dd57b3d3..0000000000 --- a/src/mbgl/renderer/render_custom_layer.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include <mbgl/renderer/render_custom_layer.hpp> -#include <mbgl/style/layers/custom_layer_impl.hpp> -#include <mbgl/renderer/bucket.hpp> - -namespace mbgl { - -RenderCustomLayer::RenderCustomLayer(const style::CustomLayer::Impl& _impl) - : RenderLayer(style::LayerType::Custom, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderCustomLayer::clone() const { - return std::make_unique<RenderCustomLayer>(*this); -} - -void RenderCustomLayer::evaluate(const PropertyEvaluationParameters&) { - passes = RenderPass::Translucent; -} - -bool RenderCustomLayer::hasTransition() const { - return false; -} - -std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { - assert(false); - return nullptr; -} - -} diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/render_fill_extrusion_layer.cpp deleted file mode 100644 index f6ba164d8c..0000000000 --- a/src/mbgl/renderer/render_fill_extrusion_layer.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> -#include <mbgl/renderer/fill_extrusion_bucket.hpp> -#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/intersection_tests.hpp> - -namespace mbgl { - -RenderFillExtrusionLayer::RenderFillExtrusionLayer(const style::FillExtrusionLayer::Impl& _impl) - : RenderLayer(style::LayerType::FillExtrusion, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderFillExtrusionLayer::clone() const { - return std::make_unique<RenderFillExtrusionLayer>(*this); -} - -std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<FillExtrusionBucket>(parameters, layers); -} - -void RenderFillExtrusionLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent - : RenderPass::None; -} - -bool RenderFillExtrusionLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -bool RenderFillExtrusionLayer::queryIntersectsFeature( - const GeometryCoordinates& queryGeometry, - const GeometryTileFeature& feature, - const float, - const float bearing, - const float pixelsToTileUnits) const { - - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( - queryGeometry, - evaluated.get<style::FillExtrusionTranslate>(), - evaluated.get<style::FillExtrusionTranslateAnchor>(), - bearing, - pixelsToTileUnits); - - return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_fill_layer.cpp b/src/mbgl/renderer/render_fill_layer.cpp deleted file mode 100644 index 1af139cded..0000000000 --- a/src/mbgl/renderer/render_fill_layer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include <mbgl/renderer/render_fill_layer.hpp> -#include <mbgl/renderer/fill_bucket.hpp> -#include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/intersection_tests.hpp> - -namespace mbgl { - -RenderFillLayer::RenderFillLayer(const style::FillLayer::Impl& _impl) - : RenderLayer(style::LayerType::Fill, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderFillLayer::clone() const { - return std::make_unique<RenderFillLayer>(*this); -} - -std::unique_ptr<Bucket> RenderFillLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<FillBucket>(parameters, layers); -} - -void RenderFillLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - if (unevaluated.get<style::FillOutlineColor>().isUndefined()) { - evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>(); - } - - passes = RenderPass::None; - - if (evaluated.get<style::FillAntialias>()) { - passes |= RenderPass::Translucent; - } - - if (!unevaluated.get<style::FillPattern>().isUndefined() - || evaluated.get<style::FillColor>().constantOr(Color()).a < 1.0f - || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f) { - passes |= RenderPass::Translucent; - } else { - passes |= RenderPass::Opaque; - } -} - -bool RenderFillLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -bool RenderFillLayer::queryIntersectsFeature( - const GeometryCoordinates& queryGeometry, - const GeometryTileFeature& feature, - const float, - const float bearing, - const float pixelsToTileUnits) const { - - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( - queryGeometry, - evaluated.get<style::FillTranslate>(), - evaluated.get<style::FillTranslateAnchor>(), - bearing, - pixelsToTileUnits); - - return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); -} - - -} diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp deleted file mode 100644 index 787211c30a..0000000000 --- a/src/mbgl/renderer/render_item.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include <mbgl/util/color.hpp> - -#include <unordered_set> -#include <vector> - -namespace mbgl { - -class RenderLayer; -class RenderSource; -class RenderTile; -class Bucket; - -namespace style { -} // namespace style - -class RenderItem { -public: - RenderItem(const RenderLayer& layer_, - std::vector<std::reference_wrapper<RenderTile>> tiles_ = {}) - : layer(layer_), tiles(std::move(tiles_)) { - } - - const RenderLayer& layer; - std::vector<std::reference_wrapper<RenderTile>> tiles; -}; - -class RenderData { -public: - Color backgroundColor; - std::unordered_set<RenderSource*> sources; - std::vector<RenderItem> order; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp index 6699f39144..eb2b74ffe0 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -1,14 +1,55 @@ #include <mbgl/renderer/render_layer.hpp> +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/renderer/layers/render_circle_layer.hpp> +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/layers/render_fill_layer.hpp> +#include <mbgl/renderer/layers/render_line_layer.hpp> +#include <mbgl/renderer/layers/render_raster_layer.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/renderer/render_tile.hpp> namespace mbgl { -RenderLayer::RenderLayer(style::LayerType type_, const style::Layer::Impl& baseImpl_) - : type(type_), baseImpl(baseImpl_) { +using namespace style; + +std::unique_ptr<RenderLayer> RenderLayer::create(Immutable<Layer::Impl> impl) { + switch (impl->type) { + case LayerType::Fill: + return std::make_unique<RenderFillLayer>(staticImmutableCast<FillLayer::Impl>(impl)); + case LayerType::Line: + return std::make_unique<RenderLineLayer>(staticImmutableCast<LineLayer::Impl>(impl)); + case LayerType::Circle: + return std::make_unique<RenderCircleLayer>(staticImmutableCast<CircleLayer::Impl>(impl)); + case LayerType::Symbol: + return std::make_unique<RenderSymbolLayer>(staticImmutableCast<SymbolLayer::Impl>(impl)); + case LayerType::Raster: + return std::make_unique<RenderRasterLayer>(staticImmutableCast<RasterLayer::Impl>(impl)); + case LayerType::Background: + return std::make_unique<RenderBackgroundLayer>(staticImmutableCast<BackgroundLayer::Impl>(impl)); + case LayerType::Custom: + return std::make_unique<RenderCustomLayer>(staticImmutableCast<CustomLayer::Impl>(impl)); + case LayerType::FillExtrusion: + return std::make_unique<RenderFillExtrusionLayer>(staticImmutableCast<FillExtrusionLayer::Impl>(impl)); + } + + // Not reachable, but placate GCC. + assert(false); + return nullptr; +} + +RenderLayer::RenderLayer(style::LayerType type_, Immutable<style::Layer::Impl> baseImpl_) + : type(type_), + baseImpl(baseImpl_) { +} + +void RenderLayer::setImpl(Immutable<style::Layer::Impl> impl) { + baseImpl = impl; } const std::string& RenderLayer::getID() const { - return baseImpl.id; + return baseImpl->id; } bool RenderLayer::hasRenderPass(RenderPass pass) const { @@ -17,9 +58,14 @@ bool RenderLayer::hasRenderPass(RenderPass pass) const { bool RenderLayer::needsRendering(float zoom) const { return passes != RenderPass::None - && baseImpl.visibility != style::VisibilityType::None - && baseImpl.minZoom <= zoom - && baseImpl.maxZoom >= zoom; + && baseImpl->visibility != style::VisibilityType::None + && baseImpl->minZoom <= zoom + && baseImpl->maxZoom >= zoom; +} + +void RenderLayer::setRenderTiles(std::vector<std::reference_wrapper<RenderTile>> tiles) { + renderTiles = std::move(tiles); } -}
\ No newline at end of file +} //namespace mbgl + diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index eea2ec1f61..dfc6bcf2fd 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -12,27 +12,27 @@ namespace mbgl { class Bucket; class BucketParameters; -class CascadeParameters; +class TransitionParameters; class PropertyEvaluationParameters; +class PaintParameters; +class RenderSource; +class RenderTile; class RenderLayer { - protected: - RenderLayer(style::LayerType, const style::Layer::Impl&); + RenderLayer(style::LayerType, Immutable<style::Layer::Impl>); const style::LayerType type; public: + static std::unique_ptr<RenderLayer> create(Immutable<style::Layer::Impl>); virtual ~RenderLayer() = default; - // Create an identical copy of this layer. - virtual std::unique_ptr<RenderLayer> clone() const = 0; - - // Partially evaluate paint properties based on a set of classes. - virtual void cascade(const CascadeParameters&) = 0; + // Begin transitions for any properties that have changed since the last frame. + virtual void transition(const TransitionParameters&) = 0; - // Fully evaluate cascaded paint properties based on a zoom level. + // Fully evaluate possibly-transitioning paint properties based on a zoom level. virtual void evaluate(const PropertyEvaluationParameters&) = 0; // Returns true if any paint properties have active transitions. @@ -61,6 +61,8 @@ public: // Checks whether this layer can be rendered. bool needsRendering(float zoom) const; + virtual void render(PaintParameters&, RenderSource*) = 0; + // Check wether the given geometry intersects // with the feature virtual bool queryIntersectsFeature( @@ -72,15 +74,21 @@ public: virtual std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const = 0; + void setRenderTiles(std::vector<std::reference_wrapper<RenderTile>>); // Private implementation - const style::Layer::Impl& baseImpl; + Immutable<style::Layer::Impl> baseImpl; + void setImpl(Immutable<style::Layer::Impl>); friend std::string layoutKey(const RenderLayer&); -protected: +protected: // Stores what render passes this layer is currently enabled for. This depends on the // evaluated StyleProperties object and is updated accordingly. RenderPass passes = RenderPass::None; + + //Stores current set of tiles to be rendered for this layer. + std::vector<std::reference_wrapper<RenderTile>> renderTiles; + }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_light.cpp b/src/mbgl/renderer/render_light.cpp index 134e1829e0..85768cff47 100644 --- a/src/mbgl/renderer/render_light.cpp +++ b/src/mbgl/renderer/render_light.cpp @@ -2,25 +2,17 @@ namespace mbgl { -RenderLight::RenderLight(std::shared_ptr<const style::Light::Impl> impl_) - : impl(std::move(impl_)) { +RenderLight::RenderLight(Immutable<style::Light::Impl> impl_) + : impl(std::move(impl_)), + transitioning(impl->properties.untransitioned()) { } -RenderLight::RenderLight(std::shared_ptr<const style::Light::Impl> impl_, const TransitioningLight transitioning_) - : impl(std::move(impl_)) - , transitioning(transitioning_) { -} - -std::unique_ptr<RenderLight> RenderLight::copy(std::shared_ptr<const style::Light::Impl> impl_) const { - return std::make_unique<RenderLight>(std::move(impl_), transitioning); -} - -void RenderLight::transition(const CascadeParameters& parameters) { - transitioning = TransitioningLight(impl->properties, std::move(transitioning), parameters); +void RenderLight::transition(const TransitionParameters& parameters) { + transitioning = impl->properties.transitioned(parameters, std::move(transitioning)); } void RenderLight::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = EvaluatedLight(transitioning, parameters); + evaluated = transitioning.evaluate(parameters); } bool RenderLight::hasTransition() const { diff --git a/src/mbgl/renderer/render_light.hpp b/src/mbgl/renderer/render_light.hpp index 275f3ae8ba..f13f925318 100644 --- a/src/mbgl/renderer/render_light.hpp +++ b/src/mbgl/renderer/render_light.hpp @@ -1,98 +1,29 @@ #pragma once #include <mbgl/style/light_impl.hpp> -#include <mbgl/style/light_properties.hpp> -#include <mbgl/renderer/transitioning_property.hpp> -#include <mbgl/renderer/cascade_parameters.hpp> -#include <mbgl/renderer/property_evaluator.hpp> -#include <mbgl/renderer/property_evaluation_parameters.hpp> -#include <mbgl/util/ignore.hpp> - -#include <memory> +#include <mbgl/util/immutable.hpp> namespace mbgl { -template <class TypeList> -class Transitioning; - -template <class... Ps> -class Transitioning<TypeList<Ps...>> : public IndexedTuple< - TypeList<Ps...>, - TypeList<TransitioningProperty<typename Ps::ValueType>...>> -{ -private: - using Properties = TypeList<Ps...>; - using Raw = IndexedTuple<Properties, Properties>; - using Super = IndexedTuple< - TypeList<Ps...>, - TypeList<TransitioningProperty<typename Ps::ValueType>...>>; - -public: - Transitioning() = default; - Transitioning(const Raw& raw, Transitioning&& prior, const CascadeParameters& params) - : Super { - TransitioningProperty<typename Ps::ValueType>( - raw.template get<Ps>().value, - std::move(prior.template get<Ps>()), - raw.template get<Ps>().transition.reverseMerge(params.transition), - params.now)... - } {} - - bool hasTransition() const { - bool result = false; - util::ignore({ result |= this->template get<Ps>().hasTransition()... }); - return result; - } -}; - -template <class TypeList> -class Evaluated; +class TransitionParameters; +class PropertyEvaluationParameters; -template <class... Ps> -class Evaluated<TypeList<Ps...>> : public IndexedTuple< - TypeList<Ps...>, - TypeList<typename Ps::Type...>> -{ -private: - using Properties = TypeList<Ps...>; - using TransitioningPs = Transitioning<Properties>; - using Super = IndexedTuple< - TypeList<Ps...>, - TypeList<typename Ps::Type...>>; - -public: - Evaluated() = default; - Evaluated(TransitioningPs& transitioning, const PropertyEvaluationParameters& params) - : Super { - transitioning.template get<Ps>() - .evaluate(PropertyEvaluator<typename Ps::Type>(params, Ps::defaultValue()), params.now)... - } {} -}; - -using TransitioningLight = Transitioning<style::LightProperties>; -using EvaluatedLight = Evaluated<style::LightProperties>; +using TransitioningLight = style::LightProperties::Unevaluated; +using EvaluatedLight = style::LightProperties::PossiblyEvaluated; class RenderLight { public: - RenderLight(std::shared_ptr<const style::Light::Impl>); - - // Creates a copy intitalized with previous transitioning light - RenderLight(std::shared_ptr<const style::Light::Impl>, const TransitioningLight); + RenderLight(Immutable<style::Light::Impl>); - // creates a copy initialized with previous transitioning - // values - std::unique_ptr<RenderLight> copy(std::shared_ptr<const style::Light::Impl>) const; - - void transition(const CascadeParameters&); + void transition(const TransitionParameters&); void evaluate(const PropertyEvaluationParameters&); bool hasTransition() const; const EvaluatedLight& getEvaluated() const; - const std::shared_ptr<const style::Light::Impl> impl; + Immutable<style::Light::Impl> impl; private: - TransitioningLight transitioning; EvaluatedLight evaluated; }; diff --git a/src/mbgl/renderer/render_line_layer.cpp b/src/mbgl/renderer/render_line_layer.cpp deleted file mode 100644 index 06c2564516..0000000000 --- a/src/mbgl/renderer/render_line_layer.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include <mbgl/renderer/render_line_layer.hpp> -#include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/style/layers/line_layer_impl.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/intersection_tests.hpp> - -namespace mbgl { - -RenderLineLayer::RenderLineLayer(const style::LineLayer::Impl& _impl) - : RenderLayer(style::LayerType::Line, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderLineLayer::clone() const { - return std::make_unique<RenderLineLayer>(*this); -} - -std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { - return std::make_unique<LineBucket>(parameters, layers, impl->layout); -} - -void RenderLineLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) { - // for scaling dasharrays - auto dashArrayParams = parameters; - dashArrayParams.z = std::floor(dashArrayParams.z); - dashLineWidth = unevaluated.evaluate<style::LineWidth>(dashArrayParams); - - evaluated = unevaluated.evaluate(parameters); - - passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0 - && evaluated.get<style::LineColor>().constantOr(Color::black()).a > 0 - && evaluated.get<style::LineWidth>() > 0) - ? RenderPass::Translucent : RenderPass::None; -} - -bool RenderLineLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) { - if (offset == 0) return {}; - - GeometryCollection newRings; - Point<double> zero(0, 0); - for (const auto& ring : rings) { - newRings.emplace_back(); - auto& newRing = newRings.back(); - - for (auto i = ring.begin(); i != ring.end(); i++) { - auto& p = *i; - - Point<double> aToB = i == ring.begin() ? - zero : - util::perp(util::unit(convertPoint<double>(p - *(i - 1)))); - Point<double> bToC = i + 1 == ring.end() ? - zero : - util::perp(util::unit(convertPoint<double>(*(i + 1) - p))); - Point<double> extrude = util::unit(aToB + bToC); - - const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y; - extrude *= (1.0 / cosHalfAngle); - - newRing.push_back(convertPoint<int16_t>(extrude * offset) + p); - } - } - - return newRings; -} - -bool RenderLineLayer::queryIntersectsFeature( - const GeometryCoordinates& queryGeometry, - const GeometryTileFeature& feature, - const float zoom, - const float bearing, - const float pixelsToTileUnits) const { - - // Translate query geometry - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( - queryGeometry, - evaluated.get<style::LineTranslate>(), - evaluated.get<style::LineTranslateAnchor>(), - bearing, - pixelsToTileUnits); - - // Evaluate function - auto offset = evaluated.get<style::LineOffset>() - .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits; - - // Apply offset to geometry - auto offsetGeometry = offsetLine(feature.getGeometries(), offset); - - // Test intersection - const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits; - return util::polygonIntersectsBufferedMultiLine( - translatedQueryGeometry.value_or(queryGeometry), - offsetGeometry.value_or(feature.getGeometries()), - halfWidth); -} - -float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { - float lineWidth = evaluated.get<style::LineWidth>(); - float gapWidth = evaluated.get<style::LineGapWidth>() - .evaluate(feature, zoom, style::LineGapWidth::defaultValue()); - if (gapWidth) { - return gapWidth + 2 * lineWidth; - } else { - return lineWidth; - } -} - - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp index d273e34ff7..5d18304129 100644 --- a/src/mbgl/renderer/render_pass.hpp +++ b/src/mbgl/renderer/render_pass.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/util/traits.hpp> +#include <mbgl/util/util.hpp> #include <cstdint> @@ -10,17 +11,18 @@ enum class RenderPass : uint8_t { None = 0, Opaque = 1 << 0, Translucent = 1 << 1, + Pass3D = 1 << 2, }; -constexpr RenderPass operator|(RenderPass a, RenderPass b) { +MBGL_CONSTEXPR RenderPass operator|(RenderPass a, RenderPass b) { return RenderPass(mbgl::underlying_type(a) | mbgl::underlying_type(b)); } -constexpr RenderPass& operator|=(RenderPass& a, RenderPass b) { +MBGL_CONSTEXPR RenderPass& operator|=(RenderPass& a, RenderPass b) { return (a = a | b); } -constexpr RenderPass operator&(RenderPass a, RenderPass b) { +MBGL_CONSTEXPR RenderPass operator&(RenderPass a, RenderPass b) { return RenderPass(mbgl::underlying_type(a) & mbgl::underlying_type(b)); } diff --git a/src/mbgl/renderer/render_raster_layer.cpp b/src/mbgl/renderer/render_raster_layer.cpp deleted file mode 100644 index 5e664e6f58..0000000000 --- a/src/mbgl/renderer/render_raster_layer.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <mbgl/renderer/render_raster_layer.hpp> -#include <mbgl/renderer/bucket.hpp> -#include <mbgl/style/layers/raster_layer_impl.hpp> - -namespace mbgl { - -RenderRasterLayer::RenderRasterLayer(const style::RasterLayer::Impl& _impl) - : RenderLayer(style::LayerType::Raster, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderRasterLayer::clone() const { - return std::make_unique<RenderRasterLayer>(*this); -} - -std::unique_ptr<Bucket> RenderRasterLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { - assert(false); - return nullptr; -} - -void RenderRasterLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - passes = evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None; -} - -bool RenderRasterLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp index 643d92fe81..7723a1c7ca 100644 --- a/src/mbgl/renderer/render_source.cpp +++ b/src/mbgl/renderer/render_source.cpp @@ -1,12 +1,42 @@ #include <mbgl/renderer/render_source.hpp> #include <mbgl/renderer/render_source_observer.hpp> +#include <mbgl/renderer/sources/render_geojson_source.hpp> +#include <mbgl/renderer/sources/render_raster_source.hpp> +#include <mbgl/renderer/sources/render_vector_source.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/annotation/render_annotation_source.hpp> +#include <mbgl/renderer/sources/render_image_source.hpp> #include <mbgl/tile/tile.hpp> namespace mbgl { +using namespace style; + +std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl) { + switch (impl->type) { + case SourceType::Vector: + return std::make_unique<RenderVectorSource>(staticImmutableCast<VectorSource::Impl>(impl)); + case SourceType::Raster: + return std::make_unique<RenderRasterSource>(staticImmutableCast<RasterSource::Impl>(impl)); + case SourceType::GeoJSON: + return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl)); + case SourceType::Video: + assert(false); + return nullptr; + case SourceType::Annotations: + return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl)); + case SourceType::Image: + return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl)); + } + + // Not reachable, but placate GCC. + assert(false); + return nullptr; +} + static RenderSourceObserver nullObserver; -RenderSource::RenderSource(const style::Source::Impl& impl) +RenderSource::RenderSource(Immutable<style::Source::Impl> impl) : baseImpl(impl), observer(&nullObserver) { } @@ -23,4 +53,8 @@ void RenderSource::onTileError(Tile& tile, std::exception_ptr error) { observer->onTileError(*this, tile.id, error); } +bool RenderSource::isEnabled() const { + return enabled; } + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index d31347579e..8293923ff6 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -6,6 +6,7 @@ #include <mbgl/util/geo.hpp> #include <mbgl/util/feature.hpp> #include <mbgl/style/source_impl.hpp> +#include <mbgl/style/layer_impl.hpp> #include <unordered_map> #include <vector> @@ -14,69 +15,73 @@ namespace mbgl { -class Painter; +class PaintParameters; class TransformState; class RenderTile; +class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; class Tile; class RenderSourceObserver; class TileParameters; -namespace algorithm { -class ClipIDGenerator; -} // namespace algorithm - class RenderSource : protected TileObserver { public: - RenderSource(const style::Source::Impl&); - virtual ~RenderSource() = default; + static std::unique_ptr<RenderSource> create(Immutable<style::Source::Impl>); - virtual bool isLoaded() const = 0; + // Check whether this source is of the given subtype. + template <class T> + bool is() const; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - virtual void updateTiles(const TileParameters&) = 0; + // Dynamically cast this source to the given subtype. + template <class T> + T* as() { + return is<T>() ? reinterpret_cast<T*>(this) : nullptr; + } - // Removes all tiles (by putting them into the cache). - virtual void removeTiles() = 0; + template <class T> + const T* as() const { + return is<T>() ? reinterpret_cast<const T*>(this) : nullptr; + } - // Remove all tiles and clear the cache. - virtual void invalidateTiles() = 0; + bool isEnabled() const; + virtual bool isLoaded() const = 0; - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - virtual void reloadTiles() = 0; + virtual void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) = 0; - virtual void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) = 0; - virtual void finishRender(Painter&) = 0; + virtual void startRender(PaintParameters&) = 0; + virtual void finishRender(PaintParameters&) = 0; - virtual std::map<UnwrappedTileID, RenderTile>& getRenderTiles() = 0; + // Returns an unsorted list of RenderTiles. + virtual std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() = 0; virtual std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const = 0; virtual std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const = 0; - virtual void setCacheSize(size_t) = 0; virtual void onLowMemory() = 0; virtual void dumpDebugLogs() const = 0; void setObserver(RenderSourceObserver*); - const style::Source::Impl& baseImpl; - bool enabled = false; + Immutable<style::Source::Impl> baseImpl; protected: + RenderSource(Immutable<style::Source::Impl>); RenderSourceObserver* observer; + bool enabled = false; + void onTileChanged(Tile&) final; void onTileError(Tile&, std::exception_ptr) final; }; diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp new file mode 100644 index 0000000000..ccf239e643 --- /dev/null +++ b/src/mbgl/renderer/render_static_data.cpp @@ -0,0 +1,67 @@ +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/program_parameters.hpp> + +namespace mbgl { + +static gl::VertexVector<FillLayoutVertex> tileVertices() { + gl::VertexVector<FillLayoutVertex> result; + result.emplace_back(FillProgram::layoutVertex({ 0, 0 })); + result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, 0 })); + result.emplace_back(FillProgram::layoutVertex({ 0, util::EXTENT })); + result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, util::EXTENT })); + return result; +} + +static gl::IndexVector<gl::Triangles> quadTriangleIndices() { + gl::IndexVector<gl::Triangles> result; + result.emplace_back(0, 1, 2); + result.emplace_back(1, 2, 3); + return result; +} + +static gl::IndexVector<gl::LineStrip> tileLineStripIndices() { + gl::IndexVector<gl::LineStrip> result; + result.emplace_back(0); + result.emplace_back(1); + result.emplace_back(3); + result.emplace_back(2); + result.emplace_back(0); + return result; +} + +static gl::VertexVector<RasterLayoutVertex> rasterVertices() { + gl::VertexVector<RasterLayoutVertex> result; + result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 })); + result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { util::EXTENT, 0 })); + result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, util::EXTENT })); + result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { util::EXTENT, util::EXTENT })); + return result; +} + +static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() { + gl::VertexVector<ExtrusionTextureLayoutVertex> result; + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 })); + return result; +} + +RenderStaticData::RenderStaticData(gl::Context& context, float pixelRatio, const optional<std::string>& programCacheDir) + : tileVertexBuffer(context.createVertexBuffer(tileVertices())), + rasterVertexBuffer(context.createVertexBuffer(rasterVertices())), + extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())), + quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())), + tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())), + programs(context, ProgramParameters { pixelRatio, false, programCacheDir }) +#ifndef NDEBUG + , overdrawPrograms(context, ProgramParameters { pixelRatio, true, programCacheDir }) +#endif +{ + tileTriangleSegments.emplace_back(0, 0, 4, 6); + tileBorderSegments.emplace_back(0, 0, 4, 5); + rasterSegments.emplace_back(0, 0, 4, 6); + extrusionTextureSegments.emplace_back(0, 0, 4, 6); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp new file mode 100644 index 0000000000..cf58c31f4d --- /dev/null +++ b/src/mbgl/renderer/render_static_data.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/util/optional.hpp> + +#include <string> + +namespace mbgl { + +class RenderStaticData { +public: + RenderStaticData(gl::Context&, float pixelRatio, const optional<std::string>& programCacheDir); + + gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer; + gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer; + gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer; + + gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer; + gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer; + + SegmentVector<FillAttributes> tileTriangleSegments; + SegmentVector<DebugAttributes> tileBorderSegments; + SegmentVector<RasterAttributes> rasterSegments; + SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments; + + optional<gl::Renderbuffer<gl::RenderbufferType::DepthComponent>> depthRenderbuffer; + bool has3D = false; + Size backendSize; + + Programs programs; + +#ifndef NDEBUG + Programs overdrawPrograms; +#endif +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_symbol_layer.cpp b/src/mbgl/renderer/render_symbol_layer.cpp deleted file mode 100644 index 30d769e032..0000000000 --- a/src/mbgl/renderer/render_symbol_layer.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include <mbgl/renderer/render_symbol_layer.hpp> -#include <mbgl/layout/symbol_layout.hpp> -#include <mbgl/renderer/bucket.hpp> -#include <mbgl/renderer/bucket_parameters.hpp> -#include <mbgl/renderer/property_evaluation_parameters.hpp> -#include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/tile/geometry_tile_data.hpp> - -namespace mbgl { - -RenderSymbolLayer::RenderSymbolLayer(const style::SymbolLayer::Impl& _impl) - : RenderLayer(style::LayerType::Symbol, _impl), - impl(&_impl) { -} - -std::unique_ptr<RenderLayer> RenderSymbolLayer::clone() const { - return std::make_unique<RenderSymbolLayer>(*this); -} - -std::unique_ptr<Bucket> RenderSymbolLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { - assert(false); // Should be calling createLayout() instead. - return nullptr; -} - -std::unique_ptr<SymbolLayout> RenderSymbolLayer::createLayout(const BucketParameters& parameters, - const std::vector<const RenderLayer*>& group, - const GeometryTileLayer& layer, - GlyphDependencies& glyphDependencies, - IconDependencies& iconDependencies) const { - return std::make_unique<SymbolLayout>(parameters, - group, - layer, - iconDependencies, - glyphDependencies); -} - -void RenderSymbolLayer::cascade(const CascadeParameters& parameters) { - unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); -} - -void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) { - evaluated = unevaluated.evaluate(parameters); - - auto hasIconOpacity = evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 || - evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0; - auto hasTextOpacity = evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 || - evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0; - - passes = ((evaluated.get<style::IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) - || (evaluated.get<style::TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) - ? RenderPass::Translucent : RenderPass::None; -} - -bool RenderSymbolLayer::hasTransition() const { - return unevaluated.hasTransition(); -} - -style::IconPaintProperties::Evaluated RenderSymbolLayer::iconPaintProperties() const { - return style::IconPaintProperties::Evaluated { - evaluated.get<style::IconOpacity>(), - evaluated.get<style::IconColor>(), - evaluated.get<style::IconHaloColor>(), - evaluated.get<style::IconHaloWidth>(), - evaluated.get<style::IconHaloBlur>(), - evaluated.get<style::IconTranslate>(), - evaluated.get<style::IconTranslateAnchor>() - }; -} - -style::TextPaintProperties::Evaluated RenderSymbolLayer::textPaintProperties() const { - return style::TextPaintProperties::Evaluated { - evaluated.get<style::TextOpacity>(), - evaluated.get<style::TextColor>(), - evaluated.get<style::TextHaloColor>(), - evaluated.get<style::TextHaloWidth>(), - evaluated.get<style::TextHaloBlur>(), - evaluated.get<style::TextTranslate>(), - evaluated.get<style::TextTranslateAnchor>() - }; -} - - -style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { - return style::SymbolPropertyValues { - layout_.get<style::IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment - layout_.get<style::IconRotationAlignment>(), - layout_.get<style::IconSize>(), - evaluated.get<style::IconTranslate>(), - evaluated.get<style::IconTranslateAnchor>(), - iconSize, - 1.0f, - evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 && - evaluated.get<style::IconHaloWidth>().constantOr(1), - evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 - }; -} - -style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { - return style::SymbolPropertyValues { - layout_.get<style::TextPitchAlignment>(), - layout_.get<style::TextRotationAlignment>(), - layout_.get<style::TextSize>(), - evaluated.get<style::TextTranslate>(), - evaluated.get<style::TextTranslateAnchor>(), - textSize, - 24.0f, - evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 && - evaluated.get<style::TextHaloWidth>().constantOr(1), - evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 - }; -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index ce59186e61..8df31f8d7c 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -1,5 +1,11 @@ #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/buckets/debug_bucket.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> #include <mbgl/map/transform_state.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/util/math.hpp> namespace mbgl { @@ -8,24 +14,26 @@ using namespace style; mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix, const std::array<float, 2>& translation, TranslateAnchorType anchor, - const TransformState& state) const { + const TransformState& state, + const bool inViewportPixelUnits) const { if (translation[0] == 0 && translation[1] == 0) { return tileMatrix; } mat4 vtxMatrix; - if (anchor == TranslateAnchorType::Viewport) { - const double sin_a = std::sin(-state.getAngle()); - const double cos_a = std::cos(-state.getAngle()); - matrix::translate(vtxMatrix, tileMatrix, - id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()), - id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()), - 0); + const float angle = inViewportPixelUnits ? + (anchor == TranslateAnchorType::Map ? state.getAngle() : 0) : + (anchor == TranslateAnchorType::Viewport ? -state.getAngle() : 0); + + Point<float> translate = util::rotate(Point<float>{ translation[0], translation[1] }, angle); + + if (inViewportPixelUnits) { + matrix::translate(vtxMatrix, tileMatrix, translate.x, translate.y, 0); } else { matrix::translate(vtxMatrix, tileMatrix, - id.pixelsToTileUnits(translation[0], state.getZoom()), - id.pixelsToTileUnits(translation[1], state.getZoom()), + id.pixelsToTileUnits(translate.x, state.getZoom()), + id.pixelsToTileUnits(translate.y, state.getZoom()), 0); } @@ -35,24 +43,107 @@ mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix, mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation, TranslateAnchorType anchor, const TransformState& state) const { - return translateVtxMatrix(matrix, translation, anchor, state); + return translateVtxMatrix(matrix, translation, anchor, state, false); } mat4 RenderTile::translatedClipMatrix(const std::array<float, 2>& translation, TranslateAnchorType anchor, const TransformState& state) const { - return translateVtxMatrix(nearClippedMatrix, translation, anchor, state); + return translateVtxMatrix(nearClippedMatrix, translation, anchor, state, false); } -void RenderTile::calculateMatrices(const mat4& projMatrix, - const mat4& projClipMatrix, - const TransformState& transform) { +void RenderTile::setMask(TileMask&& mask) { + tile.setMask(std::move(mask)); +} + +void RenderTile::startRender(PaintParameters& parameters) { + tile.upload(parameters.context); + // Calculate two matrices for this tile: matrix is the standard tile matrix; nearClippedMatrix // clips the near plane to 100 to save depth buffer precision - transform.matrixFor(matrix, id); - transform.matrixFor(nearClippedMatrix, id); - matrix::multiply(matrix, projMatrix, matrix); - matrix::multiply(nearClippedMatrix, projClipMatrix, nearClippedMatrix); + parameters.state.matrixFor(matrix, id); + parameters.state.matrixFor(nearClippedMatrix, id); + matrix::multiply(matrix, parameters.projMatrix, matrix); + matrix::multiply(nearClippedMatrix, parameters.nearClippedProjMatrix, nearClippedMatrix); +} + +void RenderTile::finishRender(PaintParameters& parameters) { + if (!used || parameters.debugOptions == MapDebugOptions::NoDebug) + return; + + static const style::Properties<>::PossiblyEvaluated properties {}; + static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) { + if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() || + tile.debugBucket->complete != tile.isComplete() || + !(tile.debugBucket->modified == tile.modified) || + !(tile.debugBucket->expires == tile.expires) || + tile.debugBucket->debugMode != parameters.debugOptions) { + tile.debugBucket = std::make_unique<DebugBucket>( + tile.id, tile.isRenderable(), tile.isComplete(), tile.modified, + tile.expires, parameters.debugOptions, parameters.context); + } + + parameters.programs.debug.draw( + parameters.context, + gl::Lines { 4.0f * parameters.pixelRatio }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(clip), + gl::ColorMode::unblended(), + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::white() } + }, + *tile.debugBucket->vertexBuffer, + *tile.debugBucket->indexBuffer, + tile.debugBucket->segments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "debug" + ); + + parameters.programs.debug.draw( + parameters.context, + gl::Lines { 2.0f * parameters.pixelRatio }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(clip), + gl::ColorMode::unblended(), + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::black() } + }, + *tile.debugBucket->vertexBuffer, + *tile.debugBucket->indexBuffer, + tile.debugBucket->segments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "debug" + ); + } + + if (parameters.debugOptions & MapDebugOptions::TileBorders) { + parameters.programs.debug.draw( + parameters.context, + gl::LineStrip { 4.0f * parameters.pixelRatio }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(clip), + gl::ColorMode::unblended(), + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::red() } + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.tileBorderIndexBuffer, + parameters.staticData.tileBorderSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "debug" + ); + } } } // namespace mbgl diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp index 02e8667eec..b498972f5c 100644 --- a/src/mbgl/renderer/render_tile.hpp +++ b/src/mbgl/renderer/render_tile.hpp @@ -4,6 +4,7 @@ #include <mbgl/util/mat4.hpp> #include <mbgl/util/clip_id.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/renderer/tile_mask.hpp> #include <array> @@ -11,8 +12,9 @@ namespace mbgl { class Tile; class TransformState; +class PaintParameters; -class RenderTile { +class RenderTile final { public: RenderTile(UnwrappedTileID id_, Tile& tile_) : id(std::move(id_)), tile(tile_) {} RenderTile(const RenderTile&) = delete; @@ -35,14 +37,15 @@ public: style::TranslateAnchorType anchor, const TransformState&) const; - void calculateMatrices(const mat4& projMatrix, - const mat4& projClipMatrix, - const TransformState&); -private: + void setMask(TileMask&&); + void startRender(PaintParameters&); + void finishRender(PaintParameters&); + mat4 translateVtxMatrix(const mat4& tileMatrix, const std::array<float, 2>& translation, style::TranslateAnchorType anchor, - const TransformState& state) const; + const TransformState& state, + const bool inViewportPixelUnits) const; }; } // namespace mbgl diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp new file mode 100644 index 0000000000..e915f5e146 --- /dev/null +++ b/src/mbgl/renderer/renderer.cpp @@ -0,0 +1,86 @@ +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/renderer_impl.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/annotation/annotation_manager.hpp> + +namespace mbgl { + +Renderer::Renderer(RendererBackend& backend, + float pixelRatio_, + FileSource& fileSource_, + Scheduler& scheduler_, + GLContextMode contextMode_, + const optional<std::string> programCacheDir_) + : impl(std::make_unique<Impl>(backend, pixelRatio_, fileSource_, scheduler_, + contextMode_, std::move(programCacheDir_))) { +} + +Renderer::~Renderer() { + BackendScope guard { impl->backend }; + impl.reset(); +} + +void Renderer::markContextLost() { + impl->markContextLost(); +} + +void Renderer::setObserver(RendererObserver* observer) { + impl->setObserver(observer); +} + +void Renderer::render(const UpdateParameters& updateParameters) { + impl->render(updateParameters); +} + +std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const { + return impl->queryRenderedFeatures(geometry, options); +} + +std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options) const { + return impl->queryRenderedFeatures({ point }, options); +} + +std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options) const { + return impl->queryRenderedFeatures( + { + box.min, + {box.max.x, box.min.y}, + box.max, + {box.min.x, box.max.y}, + box.min + }, + options + ); +} + +AnnotationIDs Renderer::queryPointAnnotations(const ScreenBox& box) const { + RenderedQueryOptions options; + options.layerIDs = {{ AnnotationManager::PointLayerID }}; + auto features = queryRenderedFeatures(box, options); + std::set<AnnotationID> set; + for (auto &feature : features) { + assert(feature.id); + assert(feature.id->is<uint64_t>()); + assert(feature.id->get<uint64_t>() <= std::numeric_limits<AnnotationID>::max()); + set.insert(static_cast<AnnotationID>(feature.id->get<uint64_t>())); + } + AnnotationIDs ids; + ids.reserve(set.size()); + std::move(set.begin(), set.end(), std::back_inserter(ids)); + return ids; +} + +std::vector<Feature> Renderer::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const { + return impl->querySourceFeatures(sourceID, options); +} + +void Renderer::dumpDebugLogs() { + impl->dumDebugLogs(); +} + +void Renderer::onLowMemory() { + BackendScope guard { impl->backend }; + impl->onLowMemory(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_backend.cpp b/src/mbgl/renderer/renderer_backend.cpp new file mode 100644 index 0000000000..159ef432b3 --- /dev/null +++ b/src/mbgl/renderer/renderer_backend.cpp @@ -0,0 +1,69 @@ +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/extension.hpp> +#include <mbgl/gl/debugging.hpp> + +#include <cassert> + +namespace mbgl { + +RendererBackend::RendererBackend() = default; + +gl::Context& RendererBackend::getContext() { + assert(BackendScope::exists()); + std::call_once(initialized, [this] { + context = std::make_unique<gl::Context>(); + context->enableDebugging(); + context->initializeExtensions( + std::bind(&RendererBackend::initializeExtension, this, std::placeholders::_1)); + }); + return *context; +} + +PremultipliedImage RendererBackend::readFramebuffer(const Size& size) const { + assert(context); + return context->readFramebuffer<PremultipliedImage>(size); +} + +void RendererBackend::assumeFramebufferBinding(const gl::FramebufferID fbo) { + getContext().bindFramebuffer.setCurrentValue(fbo); + if (fbo != ImplicitFramebufferBinding) { + assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); + } +} + +void RendererBackend::assumeViewport(int32_t x, int32_t y, const Size& size) { + getContext().viewport.setCurrentValue({ x, y, size }); + assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue()); +} + +void RendererBackend::assumeScissorTest(bool enabled) { + getContext().scissorTest.setCurrentValue(enabled); + assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue()); +} + +bool RendererBackend::implicitFramebufferBound() { + return getContext().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding; +} + +void RendererBackend::setFramebufferBinding(const gl::FramebufferID fbo) { + getContext().bindFramebuffer = fbo; + if (fbo != ImplicitFramebufferBinding) { + assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); + } +} + +void RendererBackend::setViewport(int32_t x, int32_t y, const Size& size) { + getContext().viewport = { x, y, size }; + assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue()); +} + +void RendererBackend::setScissorTest(bool enabled) { + getContext().scissorTest = enabled; + assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue()); +} + +RendererBackend::~RendererBackend() = default; + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp new file mode 100644 index 0000000000..1a828b80a3 --- /dev/null +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -0,0 +1,742 @@ +#include <mbgl/annotation/annotation_manager.hpp> +#include <mbgl/renderer/renderer_impl.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/renderer_observer.hpp> +#include <mbgl/renderer/render_source.hpp> +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/renderer/update_parameters.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/transition_parameters.hpp> +#include <mbgl/renderer/property_evaluation_parameters.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/style_diff.hpp> +#include <mbgl/renderer/query.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/gl/debugging.hpp> +#include <mbgl/geometry/line_atlas.hpp> +#include <mbgl/style/source_impl.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/text/glyph_manager.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/logging.hpp> + +namespace mbgl { + +using namespace style; + +static RendererObserver& nullObserver() { + static RendererObserver observer; + return observer; +} + +Renderer::Impl::Impl(RendererBackend& backend_, + float pixelRatio_, + FileSource& fileSource_, + Scheduler& scheduler_, + GLContextMode contextMode_, + const optional<std::string> programCacheDir_) + : backend(backend_) + , scheduler(scheduler_) + , fileSource(fileSource_) + , observer(&nullObserver()) + , contextMode(contextMode_) + , pixelRatio(pixelRatio_) + , programCacheDir(programCacheDir_) + , glyphManager(std::make_unique<GlyphManager>(fileSource)) + , imageManager(std::make_unique<ImageManager>()) + , lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })) + , imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>()) + , sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()) + , layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()) + , renderLight(makeMutable<Light::Impl>()) { + glyphManager->setObserver(this); +} + +Renderer::Impl::~Impl() { + assert(BackendScope::exists()); + + if (contextLost) { + // Signal all RenderCustomLayers that the context was lost + // before cleaning up + for (const auto& entry : renderLayers) { + RenderLayer& layer = *entry.second; + if (layer.is<RenderCustomLayer>()) { + layer.as<RenderCustomLayer>()->markContextDestroyed(); + } + } + } +}; + +void Renderer::Impl::setObserver(RendererObserver* observer_) { + observer = observer_ ? observer_ : &nullObserver(); +} + +void Renderer::Impl::render(const UpdateParameters& updateParameters) { + if (updateParameters.mode == MapMode::Still) { + // Don't load/render anyting in still mode until explicitly requested. + if (!updateParameters.stillImageRequest) { + return; + } + + // Reset zoom history state. + zoomHistory.first = true; + } + + assert(BackendScope::exists()); + + updateParameters.annotationManager.updateData(); + + const bool zoomChanged = zoomHistory.update(updateParameters.transformState.getZoom(), updateParameters.timePoint); + + const TransitionParameters transitionParameters { + updateParameters.timePoint, + updateParameters.mode == MapMode::Continuous ? updateParameters.transitionOptions : TransitionOptions() + }; + + const PropertyEvaluationParameters evaluationParameters { + zoomHistory, + updateParameters.timePoint, + updateParameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero() + }; + + const TileParameters tileParameters { + updateParameters.pixelRatio, + updateParameters.debugOptions, + updateParameters.transformState, + scheduler, + fileSource, + updateParameters.mode, + updateParameters.annotationManager, + *imageManager, + *glyphManager, + updateParameters.prefetchZoomDelta + }; + + glyphManager->setURL(updateParameters.glyphURL); + + // Update light. + const bool lightChanged = renderLight.impl != updateParameters.light; + + if (lightChanged) { + renderLight.impl = updateParameters.light; + renderLight.transition(transitionParameters); + } + + if (lightChanged || zoomChanged || renderLight.hasTransition()) { + renderLight.evaluate(evaluationParameters); + } + + + const ImageDifference imageDiff = diffImages(imageImpls, updateParameters.images); + imageImpls = updateParameters.images; + + // Remove removed images from sprite atlas. + for (const auto& entry : imageDiff.removed) { + imageManager->removeImage(entry.first); + } + + // Add added images to sprite atlas. + for (const auto& entry : imageDiff.added) { + imageManager->addImage(entry.second); + } + + // Update changed images. + for (const auto& entry : imageDiff.changed) { + imageManager->updateImage(entry.second.after); + } + + imageManager->setLoaded(updateParameters.spriteLoaded); + + + const LayerDifference layerDiff = diffLayers(layerImpls, updateParameters.layers); + layerImpls = updateParameters.layers; + + // Remove render layers for removed layers. + for (const auto& entry : layerDiff.removed) { + renderLayers.erase(entry.first); + } + + // Create render layers for newly added layers. + for (const auto& entry : layerDiff.added) { + renderLayers.emplace(entry.first, RenderLayer::create(entry.second)); + } + + // Update render layers for changed layers. + for (const auto& entry : layerDiff.changed) { + renderLayers.at(entry.first)->setImpl(entry.second.after); + } + + // Update layers for class and zoom changes. + for (const auto& entry : renderLayers) { + RenderLayer& layer = *entry.second; + const bool layerAdded = layerDiff.added.count(entry.first); + const bool layerChanged = layerDiff.changed.count(entry.first); + + if (layerAdded || layerChanged) { + layer.transition(transitionParameters); + } + + if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { + layer.evaluate(evaluationParameters); + } + } + + + const SourceDifference sourceDiff = diffSources(sourceImpls, updateParameters.sources); + sourceImpls = updateParameters.sources; + + // Remove render layers for removed sources. + for (const auto& entry : sourceDiff.removed) { + renderSources.erase(entry.first); + } + + // Create render sources for newly added sources. + for (const auto& entry : sourceDiff.added) { + std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second); + renderSource->setObserver(this); + renderSources.emplace(entry.first, std::move(renderSource)); + } + + const bool hasImageDiff = !(imageDiff.added.empty() && imageDiff.removed.empty() && imageDiff.changed.empty()); + + // Update all sources. + for (const auto& source : *sourceImpls) { + std::vector<Immutable<Layer::Impl>> filteredLayers; + bool needsRendering = false; + bool needsRelayout = false; + + for (const auto& layer : *layerImpls) { + if (layer->type == LayerType::Background || + layer->type == LayerType::Custom || + layer->source != source->id) { + continue; + } + + if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) { + needsRendering = true; + } + + if (!needsRelayout && (hasImageDiff || hasLayoutDifference(layerDiff, layer->id))) { + needsRelayout = true; + } + + filteredLayers.push_back(layer); + } + + renderSources.at(source->id)->update(source, + filteredLayers, + needsRendering, + needsRelayout, + tileParameters); + } + + transformState = updateParameters.transformState; + + if (!staticData) { + staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio, programCacheDir); + } + + PaintParameters parameters { + backend.getContext(), + pixelRatio, + contextMode, + backend, + updateParameters, + renderLight.getEvaluated(), + *staticData, + frameHistory, + *imageManager, + *lineAtlas + }; + + bool loaded = updateParameters.styleLoaded && isLoaded(); + if (updateParameters.mode == MapMode::Still && !loaded) { + return; + } + + if (renderState == RenderState::Never) { + observer->onWillStartRenderingMap(); + } + + observer->onWillStartRenderingFrame(); + + backend.updateAssumedState(); + + if (parameters.contextMode == GLContextMode::Shared) { + parameters.context.setDirtyState(); + } + + Color backgroundColor; + + class RenderItem { + public: + RenderLayer& layer; + RenderSource* source; + }; + + std::vector<RenderItem> order; + + for (auto& layerImpl : *layerImpls) { + RenderLayer* layer = getRenderLayer(layerImpl->id); + assert(layer); + + if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) { + parameters.staticData.has3D = true; + } + + if (!layer->needsRendering(zoomHistory.lastZoom)) { + continue; + } + + if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) { + const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated; + if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) { + // This is a solid background. We can use glClear(). + backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>(); + } else { + // This is a textured background, or not the bottommost layer. We need to render it with a quad. + order.emplace_back(RenderItem { *layer, nullptr }); + } + continue; + } + + if (layer->is<RenderCustomLayer>()) { + order.emplace_back(RenderItem { *layer, nullptr }); + continue; + } + + RenderSource* source = getRenderSource(layer->baseImpl->source); + if (!source) { + Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str()); + continue; + } + + const bool symbolLayer = layer->is<RenderSymbolLayer>(); + + auto sortedTiles = source->getRenderTiles(); + if (symbolLayer) { + // Sort symbol tiles in opposite y position, so tiles with overlapping symbols are drawn + // on top of each other, with lower symbols being drawn on top of higher symbols. + std::sort(sortedTiles.begin(), sortedTiles.end(), [&](const RenderTile& a, const RenderTile& b) { + Point<float> pa(a.id.canonical.x, a.id.canonical.y); + Point<float> pb(b.id.canonical.x, b.id.canonical.y); + + auto par = util::rotate(pa, parameters.state.getAngle()); + auto pbr = util::rotate(pb, parameters.state.getAngle()); + + return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x); + }); + } else { + std::sort(sortedTiles.begin(), sortedTiles.end(), + [](const auto& a, const auto& b) { return a.get().id < b.get().id; }); + } + + std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion; + for (auto& sortedTile : sortedTiles) { + auto& tile = sortedTile.get(); + if (!tile.tile.isRenderable()) { + continue; + } + + // We're not clipping symbol layers, so when we have both parents and children of symbol + // layers, we drop all children in favor of their parent to avoid duplicate labels. + // See https://github.com/mapbox/mapbox-gl-native/issues/2482 + if (symbolLayer) { + bool skip = false; + // Look back through the buckets we decided to render to find out whether there is + // already a bucket from this layer that is a parent of this tile. Tiles are ordered + // by zoom level when we obtain them from getTiles(). + for (auto it = sortedTilesForInsertion.rbegin(); + it != sortedTilesForInsertion.rend(); ++it) { + if (tile.tile.id.isChildOf(it->get().tile.id)) { + skip = true; + break; + } + } + if (skip) { + continue; + } + } + + auto bucket = tile.tile.getBucket(*layer->baseImpl); + if (bucket) { + sortedTilesForInsertion.emplace_back(tile); + tile.used = true; + } + } + layer->setRenderTiles(std::move(sortedTilesForInsertion)); + order.emplace_back(RenderItem { *layer, source }); + } + + frameHistory.record(parameters.timePoint, + parameters.state.getZoom(), + parameters.mapMode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Milliseconds(0)); + + // - UPLOAD PASS ------------------------------------------------------------------------------- + // Uploads all required buffers and images before we do any actual rendering. + { + MBGL_DEBUG_GROUP(parameters.context, "upload"); + + parameters.imageManager.upload(parameters.context, 0); + parameters.lineAtlas.upload(parameters.context, 0); + parameters.frameHistory.upload(parameters.context, 0); + + // Update all clipping IDs + upload buckets. + for (const auto& entry : renderSources) { + if (entry.second->isEnabled()) { + entry.second->startRender(parameters); + } + } + } + + // - 3D PASS ------------------------------------------------------------------------------------- + // Renders any 3D layers bottom-to-top to unique FBOs with texture attachments, but share the same + // depth rbo between them. + if (parameters.staticData.has3D) { + parameters.staticData.backendSize = parameters.backend.getFramebufferSize(); + + MBGL_DEBUG_GROUP(parameters.context, "3d"); + parameters.pass = RenderPass::Pass3D; + + if (!parameters.staticData.depthRenderbuffer || + parameters.staticData.depthRenderbuffer->size != parameters.staticData.backendSize) { + parameters.staticData.depthRenderbuffer = + parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(parameters.staticData.backendSize); + } + parameters.staticData.depthRenderbuffer->shouldClear(true); + + uint32_t i = static_cast<uint32_t>(order.size()) - 1; + for (auto it = order.begin(); it != order.end(); ++it, --i) { + parameters.currentLayer = i; + if (it->layer.hasRenderPass(parameters.pass)) { + MBGL_DEBUG_GROUP(parameters.context, it->layer.getID()); + it->layer.render(parameters, it->source); + } + } + } + + // - CLEAR ------------------------------------------------------------------------------------- + // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any + // tiles whatsoever. + { + MBGL_DEBUG_GROUP(parameters.context, "clear"); + parameters.backend.bind(); + parameters.context.clear((parameters.debugOptions & MapDebugOptions::Overdraw) + ? Color::black() + : backgroundColor, + 1.0f, + 0); + } + + // - CLIPPING MASKS ---------------------------------------------------------------------------- + // Draws the clipping masks to the stencil buffer. + { + MBGL_DEBUG_GROUP(parameters.context, "clipping masks"); + + static const style::FillPaintProperties::PossiblyEvaluated properties {}; + static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) { + parameters.staticData.programs.fill.get(properties).draw( + parameters.context, + gl::Triangles(), + gl::DepthMode::disabled(), + gl::StencilMode { + gl::StencilMode::Always(), + static_cast<int32_t>(clipID.second.reference.to_ulong()), + 0b11111111, + gl::StencilMode::Keep, + gl::StencilMode::Keep, + gl::StencilMode::Replace + }, + gl::ColorMode::disabled(), + FillProgram::UniformValues { + uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) }, + uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "clipping" + ); + } + } + +#if not MBGL_USE_GLES2 and not defined(NDEBUG) + // Render tile clip boundaries, using stencil buffer to calculate fill color. + if (parameters.debugOptions & MapDebugOptions::StencilClip) { + parameters.context.setStencilMode(gl::StencilMode::disabled()); + parameters.context.setDepthMode(gl::DepthMode::disabled()); + parameters.context.setColorMode(gl::ColorMode::unblended()); + parameters.context.program = 0; + + // Reset the value in case someone else changed it, or it's dirty. + parameters.context.pixelTransferStencil = gl::value::PixelTransferStencil::Default; + + // Read the stencil buffer + const auto viewport = parameters.context.viewport.getCurrentValue(); + auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false); + + // Scale the Stencil buffer to cover the entire color space. + auto it = image.data.get(); + auto end = it + viewport.size.width * viewport.size.height; + const auto factor = 255.0f / *std::max_element(it, end); + for (; it != end; ++it) { + *it *= factor; + } + + parameters.context.pixelZoom = { 1, 1 }; + parameters.context.rasterPos = { -1, -1, 0, 1 }; + parameters.context.drawPixels(image); + + return; + } +#endif + + // Actually render the layers + + parameters.depthRangeSize = 1 - (order.size() + 2) * parameters.numSublayers * parameters.depthEpsilon; + + // - OPAQUE PASS ------------------------------------------------------------------------------- + // Render everything top-to-bottom by using reverse iterators. Render opaque objects first. + { + parameters.pass = RenderPass::Opaque; + MBGL_DEBUG_GROUP(parameters.context, "opaque"); + + uint32_t i = 0; + for (auto it = order.rbegin(); it != order.rend(); ++it, ++i) { + parameters.currentLayer = i; + if (it->layer.hasRenderPass(parameters.pass)) { + MBGL_DEBUG_GROUP(parameters.context, it->layer.getID()); + it->layer.render(parameters, it->source); + } + } + } + + // - TRANSLUCENT PASS -------------------------------------------------------------------------- + // Make a second pass, rendering translucent objects. This time, we render bottom-to-top. + { + parameters.pass = RenderPass::Translucent; + MBGL_DEBUG_GROUP(parameters.context, "translucent"); + + uint32_t i = static_cast<uint32_t>(order.size()) - 1; + for (auto it = order.begin(); it != order.end(); ++it, --i) { + parameters.currentLayer = i; + if (it->layer.hasRenderPass(parameters.pass)) { + MBGL_DEBUG_GROUP(parameters.context, it->layer.getID()); + it->layer.render(parameters, it->source); + } + } + } + + // - DEBUG PASS -------------------------------------------------------------------------------- + // Renders debug overlays. + { + MBGL_DEBUG_GROUP(parameters.context, "debug"); + + // Finalize the rendering, e.g. by calling debug render calls per tile. + // This guarantees that we have at least one function per tile called. + // When only rendering layers via the stylesheet, it's possible that we don't + // ever visit a tile during rendering. + for (const auto& entry : renderSources) { + if (entry.second->isEnabled()) { + entry.second->finishRender(parameters); + } + } + } + +#if not MBGL_USE_GLES2 and not defined(NDEBUG) + // Render the depth buffer. + if (parameters.debugOptions & MapDebugOptions::DepthBuffer) { + parameters.context.setStencilMode(gl::StencilMode::disabled()); + parameters.context.setDepthMode(gl::DepthMode::disabled()); + parameters.context.setColorMode(gl::ColorMode::unblended()); + parameters.context.program = 0; + + // Scales the values in the depth buffer so that they cover the entire grayscale range. This + // makes it easier to spot tiny differences. + const float base = 1.0f / (1.0f - parameters.depthRangeSize); + parameters.context.pixelTransferDepth = { base, 1.0f - base }; + + // Read the stencil buffer + auto viewport = parameters.context.viewport.getCurrentValue(); + auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Depth>(viewport.size, false); + + parameters.context.pixelZoom = { 1, 1 }; + parameters.context.rasterPos = { -1, -1, 0, 1 }; + parameters.context.drawPixels(image); + } +#endif + + // TODO: Find a better way to unbind VAOs after we're done with them without introducing + // unnecessary bind(0)/bind(N) sequences. + { + MBGL_DEBUG_GROUP(parameters.context, "cleanup"); + + parameters.context.activeTextureUnit = 1; + parameters.context.texture[1] = 0; + parameters.context.activeTextureUnit = 0; + parameters.context.texture[0] = 0; + + parameters.context.bindVertexArray = 0; + } + + observer->onDidFinishRenderingFrame( + loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial, + updateParameters.mode == MapMode::Continuous && (hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION)) + ); + + if (!loaded) { + renderState = RenderState::Partial; + } else if (renderState != RenderState::Fully) { + renderState = RenderState::Fully; + observer->onDidFinishRenderingMap(); + } + + // Cleanup only after signaling completion + parameters.context.performCleanup(); +} + +std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const { + std::vector<const RenderLayer*> layers; + if (options.layerIDs) { + for (const auto& layerID : *options.layerIDs) { + if (const RenderLayer* layer = getRenderLayer(layerID)) { + layers.emplace_back(layer); + } + } + } else { + for (const auto& entry : renderLayers) { + layers.emplace_back(entry.second.get()); + } + } + + std::unordered_set<std::string> sourceIDs; + for (const RenderLayer* layer : layers) { + sourceIDs.emplace(layer->baseImpl->source); + } + + std::unordered_map<std::string, std::vector<Feature>> resultsByLayer; + for (const auto& sourceID : sourceIDs) { + if (RenderSource* renderSource = getRenderSource(sourceID)) { + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options); + std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); + } + } + + std::vector<Feature> result; + + if (resultsByLayer.empty()) { + return result; + } + + // Combine all results based on the style layer order. + for (const auto& layerImpl : *layerImpls) { + const RenderLayer* layer = getRenderLayer(layerImpl->id); + if (!layer->needsRendering(zoomHistory.lastZoom)) { + continue; + } + auto it = resultsByLayer.find(layer->baseImpl->id); + if (it != resultsByLayer.end()) { + std::move(it->second.begin(), it->second.end(), std::back_inserter(result)); + } + } + + return result; +} + +std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const { + const RenderSource* source = getRenderSource(sourceID); + if (!source) return {}; + + return source->querySourceFeatures(options); +} + +void Renderer::Impl::onLowMemory() { + assert(BackendScope::exists()); + backend.getContext().performCleanup(); + for (const auto& entry : renderSources) { + entry.second->onLowMemory(); + } + observer->onInvalidate(); +} + +void Renderer::Impl::dumDebugLogs() { + for (const auto& entry : renderSources) { + entry.second->dumpDebugLogs(); + } + + imageManager->dumpDebugLogs(); +} + +RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) { + auto it = renderLayers.find(id); + return it != renderLayers.end() ? it->second.get() : nullptr; +} + +const RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) const { + auto it = renderLayers.find(id); + return it != renderLayers.end() ? it->second.get() : nullptr; +} + +RenderSource* Renderer::Impl::getRenderSource(const std::string& id) const { + auto it = renderSources.find(id); + return it != renderSources.end() ? it->second.get() : nullptr; +} + +bool Renderer::Impl::hasTransitions() const { + if (renderLight.hasTransition()) { + return true; + } + + for (const auto& entry : renderLayers) { + if (entry.second->hasTransition()) { + return true; + } + } + + return false; +} + +bool Renderer::Impl::isLoaded() const { + for (const auto& entry: renderSources) { + if (!entry.second->isLoaded()) { + return false; + } + } + + if (!imageManager->isLoaded()) { + return false; + } + + return true; +} + +void Renderer::Impl::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { + Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s", + glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str()); + observer->onResourceError(error); +} + +void Renderer::Impl::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) { + Log::Error(Event::Style, "Failed to load tile %s for source %s: %s", + util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str()); + observer->onResourceError(error); +} + +void Renderer::Impl::onTileChanged(RenderSource&, const OverscaledTileID&) { + observer->onInvalidate(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp new file mode 100644 index 0000000000..30e7f70722 --- /dev/null +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/render_source_observer.hpp> +#include <mbgl/renderer/render_light.hpp> +#include <mbgl/renderer/frame_history.hpp> +#include <mbgl/style/image.hpp> +#include <mbgl/style/source.hpp> +#include <mbgl/style/layer.hpp> +#include <mbgl/map/transform_state.hpp> +#include <mbgl/map/zoom_history.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/text/glyph_manager_observer.hpp> + +#include <memory> +#include <string> +#include <vector> + +namespace mbgl { + +class RendererBackend; +class RendererObserver; +class RenderSource; +class RenderLayer; +class UpdateParameters; +class RenderStaticData; +class RenderedQueryOptions; +class SourceQueryOptions; +class FileSource; +class Scheduler; +class GlyphManager; +class ImageManager; +class LineAtlas; + +class Renderer::Impl : public GlyphManagerObserver, + public RenderSourceObserver{ +public: + Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode, + const optional<std::string> programCacheDir); + ~Impl() final; + + void markContextLost() { + contextLost = true; + }; + + void setObserver(RendererObserver*); + + void render(const UpdateParameters&); + + std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&) const; + std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const; + + void onLowMemory(); + void dumDebugLogs(); + +private: + bool isLoaded() const; + bool hasTransitions() const; + + RenderSource* getRenderSource(const std::string& id) const; + + RenderLayer* getRenderLayer(const std::string& id); + const RenderLayer* getRenderLayer(const std::string& id) const; + + // GlyphManagerObserver implementation. + void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override; + + // RenderSourceObserver implementation. + void onTileChanged(RenderSource&, const OverscaledTileID&) override; + void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; + + friend class Renderer; + + RendererBackend& backend; + Scheduler& scheduler; + FileSource& fileSource; + + RendererObserver* observer; + + const GLContextMode contextMode; + const float pixelRatio; + const optional<std::string> programCacheDir; + + enum class RenderState { + Never, + Partial, + Fully, + }; + + RenderState renderState = RenderState::Never; + FrameHistory frameHistory; + ZoomHistory zoomHistory; + TransformState transformState; + + std::unique_ptr<GlyphManager> glyphManager; + std::unique_ptr<ImageManager> imageManager; + std::unique_ptr<LineAtlas> lineAtlas; + std::unique_ptr<RenderStaticData> staticData; + + Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls; + Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls; + Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls; + + std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources; + std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers; + RenderLight renderLight; + + bool contextLost = false; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_observer.hpp b/src/mbgl/renderer/renderer_observer.hpp new file mode 100644 index 0000000000..551b5c803e --- /dev/null +++ b/src/mbgl/renderer/renderer_observer.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include <exception> + +namespace mbgl { + +class RendererObserver { +public: + virtual ~RendererObserver() = default; + + enum class RenderMode : uint32_t { + Partial, + Full + }; + + // Signals that a repaint is required + virtual void onInvalidate() {} + + // Resource failed to download / parse + virtual void onResourceError(std::exception_ptr) {} + + // First frame + virtual void onWillStartRenderingMap() {} + + // Start of frame, initial is the first frame for this map + virtual void onWillStartRenderingFrame() {} + + // End of frame, boolean flags that a repaint is required + virtual void onDidFinishRenderingFrame(RenderMode, bool) {} + + // Final frame + virtual void onDidFinishRenderingMap() {} +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index 2b1eeea73b..504db78ea3 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -1,6 +1,8 @@ #include <mbgl/renderer/sources/render_geojson_source.hpp> #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/tile/geojson_tile.hpp> +#include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/algorithm/generate_clip_ids.hpp> #include <mbgl/algorithm/generate_clip_ids_impl.hpp> @@ -9,81 +11,87 @@ namespace mbgl { using namespace style; -RenderGeoJSONSource::RenderGeoJSONSource(const style::GeoJSONSource::Impl& impl_) - : RenderSource(impl_), - impl(impl_) { +RenderGeoJSONSource::RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl> impl_) + : RenderSource(impl_) { tilePyramid.setObserver(this); } -bool RenderGeoJSONSource::isLoaded() const { - return tilePyramid.isLoaded(); -} - -void RenderGeoJSONSource::invalidateTiles() { - tilePyramid.invalidateTiles(); -} - -void RenderGeoJSONSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { - generator.update(tilePyramid.getRenderTiles()); - tilePyramid.startRender(projMatrix, clipMatrix, transform); +const style::GeoJSONSource::Impl& RenderGeoJSONSource::impl() const { + return static_cast<const style::GeoJSONSource::Impl&>(*baseImpl); } -void RenderGeoJSONSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); +bool RenderGeoJSONSource::isLoaded() const { + return tilePyramid.isLoaded(); } -std::map<UnwrappedTileID, RenderTile>& RenderGeoJSONSource::getRenderTiles() { - return tilePyramid.getRenderTiles(); -} +void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters) { + std::swap(baseImpl, baseImpl_); -void RenderGeoJSONSource::updateTiles(const TileParameters& parameters) { - GeoJSONData* data_ = impl.getData(); + enabled = needsRendering; - if (!data_) { - return; - } + GeoJSONData* data_ = impl().getData(); if (data_ != data) { data = data_; tilePyramid.cache.clear(); - for (auto const& item : tilePyramid.tiles) { - static_cast<GeoJSONTile*>(item.second.get())->updateData(data->getTile(item.first.canonical)); + if (data) { + const uint8_t maxZ = impl().getZoomRange().max; + for (const auto& pair : tilePyramid.tiles) { + if (pair.first.canonical.z <= maxZ) { + static_cast<GeoJSONTile*>(pair.second.get())->updateData(data->getTile(pair.first.canonical)); + } + } } } - tilePyramid.updateTiles(parameters, - SourceType::GeoJSON, - util::tileSize, - impl.getZoomRange(), - [&] (const OverscaledTileID& tileID) { - return std::make_unique<GeoJSONTile>(tileID, impl.id, parameters, data->getTile(tileID.canonical)); - }); + if (!data) { + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + return; + } + + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::GeoJSON, + util::tileSize, + impl().getZoomRange(), + [&] (const OverscaledTileID& tileID) { + return std::make_unique<GeoJSONTile>(tileID, impl().id, parameters, data->getTile(tileID.canonical)); + }); } -void RenderGeoJSONSource::removeTiles() { - tilePyramid.removeTiles(); +void RenderGeoJSONSource::startRender(PaintParameters& parameters) { + parameters.clipIDGenerator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderGeoJSONSource::reloadTiles() { - tilePyramid.reloadTiles(); +void RenderGeoJSONSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); +} + +std::vector<std::reference_wrapper<RenderTile>> RenderGeoJSONSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); } std::unordered_map<std::string, std::vector<Feature>> RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, options); + const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const { return tilePyramid.querySourceFeatures(options); } -void RenderGeoJSONSource::setCacheSize(size_t size) { - tilePyramid.setCacheSize(size); -} - void RenderGeoJSONSource::onLowMemory() { tilePyramid.onLowMemory(); } diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index 262ab29276..72ab4879ef 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -12,48 +12,43 @@ class GeoJSONData; class RenderGeoJSONSource : public RenderSource { public: - RenderGeoJSONSource(const style::GeoJSONSource::Impl&); + RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl>); bool isLoaded() const final; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&) final; + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; - // Removes all tiles (by putting them into the cache). - void removeTiles() final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; - // Remove all tiles and clear the cache. - void invalidateTiles() final; - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles() final; - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) final; - void finishRender(Painter&) final; - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final; + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final; std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; private: - const style::GeoJSONSource::Impl& impl; + const style::GeoJSONSource::Impl& impl() const; + TilePyramid tilePyramid; - style::GeoJSONData* data; + style::GeoJSONData* data = nullptr; }; +template <> +inline bool RenderSource::is<RenderGeoJSONSource>() const { + return baseImpl->type == SourceType::GeoJSON; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp new file mode 100644 index 0000000000..9140e01711 --- /dev/null +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -0,0 +1,214 @@ +#include <mbgl/map/transform_state.hpp> +#include <mbgl/math/log2.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/sources/render_image_source.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/util/tile_coordinate.hpp> +#include <mbgl/util/tile_cover.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/constants.hpp> + +namespace mbgl { + +using namespace style; + +RenderImageSource::RenderImageSource(Immutable<style::ImageSource::Impl> impl_) + : RenderSource(impl_) { +} + +RenderImageSource::~RenderImageSource() = default; + +const style::ImageSource::Impl& RenderImageSource::impl() const { + return static_cast<const style::ImageSource::Impl&>(*baseImpl); +} + +bool RenderImageSource::isLoaded() const { + return !!bucket; +} + +void RenderImageSource::startRender(PaintParameters& parameters) { + if (!isLoaded()) { + return; + } + + matrices.clear(); + + for (size_t i = 0; i < tileIds.size(); i++) { + mat4 matrix; + matrix::identity(matrix); + parameters.state.matrixFor(matrix, tileIds[i]); + matrix::multiply(matrix, parameters.projMatrix, matrix); + matrices.push_back(matrix); + } + + if (bucket->needsUpload()) { + bucket->upload(parameters.context); + } +} + +void RenderImageSource::finishRender(PaintParameters& parameters) { + if (!isLoaded() || !(parameters.debugOptions & MapDebugOptions::TileBorders)) { + return; + } + + static const style::Properties<>::PossiblyEvaluated properties {}; + static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + for (auto matrix : matrices) { + parameters.programs.debug.draw( + parameters.context, + gl::LineStrip { 4.0f * parameters.pixelRatio }, + gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + gl::ColorMode::unblended(), + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::red() } + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.tileBorderIndexBuffer, + parameters.staticData.tileBorderSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + "debug" + ); + } +} + +std::unordered_map<std::string, std::vector<Feature>> +RenderImageSource::queryRenderedFeatures(const ScreenLineString&, + const TransformState&, + const std::vector<const RenderLayer*>&, + const RenderedQueryOptions&) const { + return std::unordered_map<std::string, std::vector<Feature>> {}; +} + +std::vector<Feature> RenderImageSource::querySourceFeatures(const SourceQueryOptions&) const { + return {}; +} + +void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>&, + const bool needsRendering, + const bool, + const TileParameters& parameters) { + enabled = needsRendering; + if (!needsRendering) { + return; + } + + auto transformState = parameters.transformState; + std::swap(baseImpl, baseImpl_); + + auto coords = impl().getCoordinates(); + std::shared_ptr<PremultipliedImage> image = impl().getImage(); + + if (!image || !image->valid()) { + enabled = false; + return; + } + + auto size = transformState.getSize(); + const double viewportHeight = size.height; + + // Compute the screen coordinates at wrap=0 for the given LatLng + ScreenCoordinate nePixel = { -INFINITY, -INFINITY }; + ScreenCoordinate swPixel = { INFINITY, INFINITY }; + + for (LatLng latLng : coords) { + ScreenCoordinate pixel = transformState.latLngToScreenCoordinate(latLng); + swPixel.x = std::min(swPixel.x, pixel.x); + nePixel.x = std::max(nePixel.x, pixel.x); + swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y); + nePixel.y = std::max(nePixel.y, viewportHeight - pixel.y); + } + const double width = nePixel.x - swPixel.x; + const double height = nePixel.y - swPixel.y; + + // Don't bother drawing the ImageSource unless it occupies >4 screen pixels + enabled = (width * height > 4); + if (!enabled) { + return; + } + + // Calculate the optimum zoom level to determine the tile ids to use for transforms + double minScale = INFINITY; + double scaleX = double(size.width) / width; + double scaleY = double(size.height) / height; + minScale = util::min(scaleX, scaleY); + double zoom = transformState.getZoom() + util::log2(minScale); + zoom = std::floor(util::clamp(zoom, transformState.getMinZoom(), transformState.getMaxZoom())); + auto imageBounds = LatLngBounds::hull(coords[0], coords[1]); + imageBounds.extend(coords[2]); + imageBounds.extend(coords[3]); + auto tileCover = util::tileCover(imageBounds, zoom); + tileIds.clear(); + tileIds.push_back(tileCover[0]); + bool hasVisibleTile = false; + + // Add additional wrapped tile ids if neccessary + auto idealTiles = util::tileCover(transformState, transformState.getZoom()); + for (auto tile : idealTiles) { + if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) { + tileIds.push_back({ tile.wrap, tileCover[0].canonical }); + hasVisibleTile = true; + } + else if (!hasVisibleTile) { + for (auto coveringTile: tileCover) { + if(coveringTile.canonical == tile.canonical || + coveringTile.canonical.isChildOf(tile.canonical) || + tile.canonical.isChildOf(coveringTile.canonical)) { + hasVisibleTile = true; + } + } + } + } + + enabled = hasVisibleTile; + if (!enabled) { + return; + } + + // Calculate Geometry Coordinates based on tile cover at ideal zoom + GeometryCoordinates geomCoords; + for (auto latLng : coords) { + auto tc = TileCoordinate::fromLatLng(0, latLng); + auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tc.p); + geomCoords.push_back(gc); + } + if (!bucket) { + bucket = std::make_unique<RasterBucket>(image); + } else { + bucket->clear(); + if (image != bucket->image) { + bucket->setImage(image); + } + } + + // Set Bucket Vertices, Indices, and segments + bucket->vertices.emplace_back( + RasterProgram::layoutVertex({ geomCoords[0].x, geomCoords[0].y }, { 0, 0 })); + bucket->vertices.emplace_back( + RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { util::EXTENT, 0 })); + bucket->vertices.emplace_back( + RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT })); + bucket->vertices.emplace_back( + RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { util::EXTENT, util::EXTENT })); + + bucket->indices.emplace_back(0, 1, 2); + bucket->indices.emplace_back(1, 2, 3); + + bucket->segments.emplace_back(0, 0, 4, 6); +} + +void RenderImageSource::dumpDebugLogs() const { + Log::Info(Event::General, "RenderImageSource::id: %s", impl().id.c_str()); + Log::Info(Event::General, "RenderImageSource::loaded: %s", isLoaded() ? "yes" : "no"); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp new file mode 100644 index 0000000000..7b69d09fa7 --- /dev/null +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include <mbgl/renderer/render_source.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/style/sources/image_source_impl.hpp> + +namespace mbgl { + +class RasterBucket; + +class RenderImageSource : public RenderSource { +public: + RenderImageSource(Immutable<style::ImageSource::Impl>); + ~RenderImageSource() override; + + bool isLoaded() const final; + + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; + + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; + + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final { + return {}; + } + + std::unordered_map<std::string, std::vector<Feature>> + queryRenderedFeatures(const ScreenLineString& geometry, + const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options) const final; + + std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; + + void onLowMemory() final { + } + void dumpDebugLogs() const final; + +private: + friend class RenderRasterLayer; + + const style::ImageSource::Impl& impl() const; + + std::vector<UnwrappedTileID> tileIds; + std::unique_ptr<RasterBucket> bucket; + std::vector<mat4> matrices; +}; + +template <> +inline bool RenderSource::is<RenderImageSource>() const { + return baseImpl->type == SourceType::Image; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index c5a29eebf5..bcd719365d 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -1,39 +1,35 @@ #include <mbgl/renderer/sources/render_raster_source.hpp> #include <mbgl/renderer/render_tile.hpp> #include <mbgl/tile/raster_tile.hpp> +#include <mbgl/algorithm/update_tile_masks.hpp> namespace mbgl { using namespace style; -RenderRasterSource::RenderRasterSource(const style::RasterSource::Impl& impl_) - : RenderSource(impl_), - impl(impl_) { +RenderRasterSource::RenderRasterSource(Immutable<style::RasterSource::Impl> impl_) + : RenderSource(impl_) { tilePyramid.setObserver(this); } -bool RenderRasterSource::isLoaded() const { - return tilePyramid.isLoaded(); -} - -void RenderRasterSource::invalidateTiles() { - tilePyramid.invalidateTiles(); +const style::RasterSource::Impl& RenderRasterSource::impl() const { + return static_cast<const style::RasterSource::Impl&>(*baseImpl); } -void RenderRasterSource::startRender(algorithm::ClipIDGenerator&, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { - tilePyramid.startRender(projMatrix, clipMatrix, transform); +bool RenderRasterSource::isLoaded() const { + return tilePyramid.isLoaded(); } -void RenderRasterSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); -} +void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters) { + std::swap(baseImpl, baseImpl_); -std::map<UnwrappedTileID, RenderTile>& RenderRasterSource::getRenderTiles() { - return tilePyramid.getRenderTiles(); -} + enabled = needsRendering; -void RenderRasterSource::updateTiles(const TileParameters& parameters) { - optional<Tileset> tileset = impl.getTileset(); + optional<Tileset> tileset = impl().getTileset(); if (!tileset) { return; @@ -41,41 +37,51 @@ void RenderRasterSource::updateTiles(const TileParameters& parameters) { if (tileURLTemplates != tileset->tiles) { tileURLTemplates = tileset->tiles; - tilePyramid.invalidateTiles(); + + // TODO: this removes existing buckets, and will cause flickering. + // Should instead refresh tile data in place. + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + tilePyramid.cache.clear(); } - tilePyramid.updateTiles(parameters, - SourceType::Raster, - impl.getTileSize(), - tileset->zoomRange, - [&] (const OverscaledTileID& tileID) { - return std::make_unique<RasterTile>(tileID, parameters, *tileset); - }); + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::Raster, + impl().getTileSize(), + tileset->zoomRange, + [&] (const OverscaledTileID& tileID) { + return std::make_unique<RasterTile>(tileID, parameters, *tileset); + }); +} + +void RenderRasterSource::startRender(PaintParameters& parameters) { + algorithm::updateTileMasks(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderRasterSource::removeTiles() { - tilePyramid.removeTiles(); +void RenderRasterSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); } -void RenderRasterSource::reloadTiles() { - tilePyramid.reloadTiles(); +std::vector<std::reference_wrapper<RenderTile>> RenderRasterSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); } std::unordered_map<std::string, std::vector<Feature>> RenderRasterSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions&) const { - return {}; + return std::unordered_map<std::string, std::vector<Feature>> {}; } std::vector<Feature> RenderRasterSource::querySourceFeatures(const SourceQueryOptions&) const { return {}; } -void RenderRasterSource::setCacheSize(size_t size) { - tilePyramid.setCacheSize(size); -} - void RenderRasterSource::onLowMemory() { tilePyramid.onLowMemory(); } diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 5690ba80ea..01de812309 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -8,48 +8,43 @@ namespace mbgl { class RenderRasterSource : public RenderSource { public: - RenderRasterSource(const style::RasterSource::Impl&); + RenderRasterSource(Immutable<style::RasterSource::Impl>); bool isLoaded() const final; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&) final; + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; - // Removes all tiles (by putting them into the cache). - void removeTiles() final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; - // Remove all tiles and clear the cache. - void invalidateTiles() final; - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles() final; - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) final; - void finishRender(Painter&) final; - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final; + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final; std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; private: - const style::RasterSource::Impl& impl; + const style::RasterSource::Impl& impl() const; + TilePyramid tilePyramid; optional<std::vector<std::string>> tileURLTemplates; }; +template <> +inline bool RenderSource::is<RenderRasterSource>() const { + return baseImpl->type == SourceType::Raster; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index 0db4698a81..ca3071c6b0 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -1,5 +1,6 @@ #include <mbgl/renderer/sources/render_vector_source.hpp> #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/tile/vector_tile.hpp> #include <mbgl/algorithm/generate_clip_ids.hpp> @@ -9,35 +10,29 @@ namespace mbgl { using namespace style; -RenderVectorSource::RenderVectorSource(const style::VectorSource::Impl& impl_) - : RenderSource(impl_), - impl(impl_) { +RenderVectorSource::RenderVectorSource(Immutable<style::VectorSource::Impl> impl_) + : RenderSource(impl_) { tilePyramid.setObserver(this); } -bool RenderVectorSource::isLoaded() const { - return tilePyramid.isLoaded(); +const style::VectorSource::Impl& RenderVectorSource::impl() const { + return static_cast<const style::VectorSource::Impl&>(*baseImpl); } -void RenderVectorSource::invalidateTiles() { - tilePyramid.invalidateTiles(); +bool RenderVectorSource::isLoaded() const { + return tilePyramid.isLoaded(); } -void RenderVectorSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) { - generator.update(tilePyramid.getRenderTiles()); - tilePyramid.startRender(projMatrix, clipMatrix, transform); -} +void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_, + const std::vector<Immutable<Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters) { + std::swap(baseImpl, baseImpl_); -void RenderVectorSource::finishRender(Painter& painter) { - tilePyramid.finishRender(painter); -} + enabled = needsRendering; -std::map<UnwrappedTileID, RenderTile>& RenderVectorSource::getRenderTiles() { - return tilePyramid.getRenderTiles(); -} - -void RenderVectorSource::updateTiles(const TileParameters& parameters) { - optional<Tileset> tileset = impl.getTileset(); + optional<Tileset> tileset = impl().getTileset(); if (!tileset) { return; @@ -45,41 +40,51 @@ void RenderVectorSource::updateTiles(const TileParameters& parameters) { if (tileURLTemplates != tileset->tiles) { tileURLTemplates = tileset->tiles; - tilePyramid.invalidateTiles(); + + // TODO: this removes existing buckets, and will cause flickering. + // Should instead refresh tile data in place. + tilePyramid.tiles.clear(); + tilePyramid.renderTiles.clear(); + tilePyramid.cache.clear(); } - tilePyramid.updateTiles(parameters, - SourceType::Vector, - util::tileSize, - tileset->zoomRange, - [&] (const OverscaledTileID& tileID) { - return std::make_unique<VectorTile>(tileID, impl.id, parameters, *tileset); - }); + tilePyramid.update(layers, + needsRendering, + needsRelayout, + parameters, + SourceType::Vector, + util::tileSize, + tileset->zoomRange, + [&] (const OverscaledTileID& tileID) { + return std::make_unique<VectorTile>(tileID, impl().id, parameters, *tileset); + }); } -void RenderVectorSource::removeTiles() { - tilePyramid.removeTiles(); +void RenderVectorSource::startRender(PaintParameters& parameters) { + parameters.clipIDGenerator.update(tilePyramid.getRenderTiles()); + tilePyramid.startRender(parameters); } -void RenderVectorSource::reloadTiles() { - tilePyramid.reloadTiles(); +void RenderVectorSource::finishRender(PaintParameters& parameters) { + tilePyramid.finishRender(parameters); +} + +std::vector<std::reference_wrapper<RenderTile>> RenderVectorSource::getRenderTiles() { + return tilePyramid.getRenderTiles(); } std::unordered_map<std::string, std::vector<Feature>> RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, options); + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const { return tilePyramid.querySourceFeatures(options); } -void RenderVectorSource::setCacheSize(size_t size) { - tilePyramid.setCacheSize(size); -} - void RenderVectorSource::onLowMemory() { tilePyramid.onLowMemory(); } diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 36d75e0982..5e5c6d1108 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -8,48 +8,43 @@ namespace mbgl { class RenderVectorSource : public RenderSource { public: - RenderVectorSource(const style::VectorSource::Impl&); + RenderVectorSource(Immutable<style::VectorSource::Impl>); bool isLoaded() const final; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&) final; + void update(Immutable<style::Source::Impl>, + const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&) final; - // Removes all tiles (by putting them into the cache). - void removeTiles() final; + void startRender(PaintParameters&) final; + void finishRender(PaintParameters&) final; - // Remove all tiles and clear the cache. - void invalidateTiles() final; - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles() final; - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&) final; - void finishRender(Painter&) final; - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final; + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final; std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; - void setCacheSize(size_t) final; void onLowMemory() final; void dumpDebugLogs() const final; private: - const style::VectorSource::Impl& impl; + const style::VectorSource::Impl& impl() const; + TilePyramid tilePyramid; optional<std::vector<std::string>> tileURLTemplates; }; +template <> +inline bool RenderSource::is<RenderVectorSource>() const { + return baseImpl->type == SourceType::Vector; +} + } // namespace mbgl diff --git a/src/mbgl/renderer/style_diff.cpp b/src/mbgl/renderer/style_diff.cpp new file mode 100644 index 0000000000..0017280310 --- /dev/null +++ b/src/mbgl/renderer/style_diff.cpp @@ -0,0 +1,79 @@ +#include <mbgl/renderer/style_diff.hpp> +#include <mbgl/style/layer_impl.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/util/variant.hpp> +#include <mbgl/util/longest_common_subsequence.hpp> + +namespace mbgl { + +template <class T, class Eq> +StyleDifference<T> diff(const Immutable<std::vector<T>>& a, + const Immutable<std::vector<T>>& b, + const Eq& eq) { + StyleDifference<T> result; + + if (a == b) { + return result; + } + + std::vector<T> lcs; + + longest_common_subsequence(a->begin(), a->end(), b->begin(), b->end(), std::back_inserter(lcs), eq); + + auto aIt = a->begin(); + auto bIt = b->begin(); + auto lIt = lcs.begin(); + + while (aIt != a->end() || bIt != b->end()) { + if (aIt != a->end() && (lIt == lcs.end() || !eq(*lIt, *aIt))) { + result.removed.emplace((*aIt)->id, *aIt); + aIt++; + } else if (bIt != b->end() && (lIt == lcs.end() || !eq(*lIt, *bIt))) { + result.added.emplace((*bIt)->id, *bIt); + bIt++; + } else { + if (aIt->get() != bIt->get()) { + result.changed.emplace((*bIt)->id, StyleChange<T> { *aIt, *bIt }); + } + aIt++; + bIt++; + lIt++; + } + } + + return result; +} + +ImageDifference diffImages(const Immutable<std::vector<ImmutableImage>>& a, + const Immutable<std::vector<ImmutableImage>>& b) { + return diff(a, b, [] (const ImmutableImage& lhs, const ImmutableImage& rhs) { + return lhs->id == rhs->id; + }); +} + +SourceDifference diffSources(const Immutable<std::vector<ImmutableSource>>& a, + const Immutable<std::vector<ImmutableSource>>& b) { + return diff(a, b, [] (const ImmutableSource& lhs, const ImmutableSource& rhs) { + return std::tie(lhs->id, lhs->type) + == std::tie(rhs->id, rhs->type); + }); +} + +LayerDifference diffLayers(const Immutable<std::vector<ImmutableLayer>>& a, + const Immutable<std::vector<ImmutableLayer>>& b) { + return diff(a, b, [] (const ImmutableLayer& lhs, const ImmutableLayer& rhs) { + return std::tie(lhs->id, lhs->type) + == std::tie(rhs->id, rhs->type); + }); +} + +bool hasLayoutDifference(const LayerDifference& layerDiff, const std::string& layerID) { + if (layerDiff.added.count(layerID)) + return true; + const auto it = layerDiff.changed.find(layerID); + if (it == layerDiff.changed.end()) + return false; + return it->second.before->hasLayoutDifference(*it->second.after); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/style_diff.hpp b/src/mbgl/renderer/style_diff.hpp new file mode 100644 index 0000000000..a5b42fc662 --- /dev/null +++ b/src/mbgl/renderer/style_diff.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include <mbgl/style/image_impl.hpp> +#include <mbgl/style/source_impl.hpp> +#include <mbgl/style/layer_impl.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/util/variant.hpp> + +#include <unordered_map> + +namespace mbgl { + +template <class T> +class StyleChange { +public: + T before; + T after; +}; + +template <class T> +class StyleDifference { +public: + std::unordered_map<std::string, T> added; + std::unordered_map<std::string, T> removed; + std::unordered_map<std::string, StyleChange<T>> changed; +}; + +using ImmutableImage = Immutable<style::Image::Impl>; +using ImageDifference = StyleDifference<ImmutableImage>; + +ImageDifference diffImages(const Immutable<std::vector<ImmutableImage>>&, + const Immutable<std::vector<ImmutableImage>>&); + +using ImmutableSource = Immutable<style::Source::Impl>; +using SourceDifference = StyleDifference<ImmutableSource>; + +SourceDifference diffSources(const Immutable<std::vector<ImmutableSource>>&, + const Immutable<std::vector<ImmutableSource>>&); + +using ImmutableLayer = Immutable<style::Layer::Impl>; +using LayerDifference = StyleDifference<ImmutableLayer>; + +LayerDifference diffLayers(const Immutable<std::vector<ImmutableLayer>>&, + const Immutable<std::vector<ImmutableLayer>>&); + +bool hasLayoutDifference(const LayerDifference&, const std::string& layerID); + +} // namespace mbgl diff --git a/src/mbgl/renderer/tile_mask.hpp b/src/mbgl/renderer/tile_mask.hpp new file mode 100644 index 0000000000..5f24d63ba4 --- /dev/null +++ b/src/mbgl/renderer/tile_mask.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include <mbgl/tile/tile_id.hpp> + +#include <set> + +namespace mbgl { + +// A TileMask is a set of TileIDs that describe what part of a tile should be rendered. It omits +// those parts of the tile that are covered by other/better tiles. If the entire tile should be +// rendered, it contains the { 0, 0, 0 } tile. If it's empty, no part of the tile will be rendered. +// TileMasks are typically generated with algorithm::updateTileMasks(). +using TileMask = std::set<CanonicalTileID>; + +} // namespace mbgl diff --git a/src/mbgl/renderer/tile_parameters.hpp b/src/mbgl/renderer/tile_parameters.hpp index 8f04baaec5..665c7490d2 100644 --- a/src/mbgl/renderer/tile_parameters.hpp +++ b/src/mbgl/renderer/tile_parameters.hpp @@ -8,40 +8,21 @@ class TransformState; class Scheduler; class FileSource; class AnnotationManager; - -namespace style { -class Style; -} // namespace style +class ImageManager; +class GlyphManager; class TileParameters { public: - TileParameters(float pixelRatio_, - MapDebugOptions debugOptions_, - const TransformState& transformState_, - Scheduler& workerScheduler_, - FileSource& fileSource_, - const MapMode mode_, - AnnotationManager& annotationManager_, - style::Style& style_) - : pixelRatio(pixelRatio_), - debugOptions(debugOptions_), - transformState(transformState_), - workerScheduler(workerScheduler_), - fileSource(fileSource_), - mode(mode_), - annotationManager(annotationManager_), - style(style_) {} - - float pixelRatio; - MapDebugOptions debugOptions; + const float pixelRatio; + const MapDebugOptions debugOptions; const TransformState& transformState; Scheduler& workerScheduler; FileSource& fileSource; const MapMode mode; AnnotationManager& annotationManager; - - // TODO: remove - style::Style& style; + ImageManager& imageManager; + GlyphManager& glyphManager; + const uint8_t prefetchZoomDelta; }; } // namespace mbgl diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 144afcb4f6..6cd9bd9ebd 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -1,10 +1,10 @@ #include <mbgl/renderer/tile_pyramid.hpp> #include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/renderer/render_source.hpp> #include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/query.hpp> #include <mbgl/map/transform.hpp> -#include <mbgl/map/query.hpp> #include <mbgl/text/placement_config.hpp> #include <mbgl/math/clamp.hpp> #include <mbgl/util/tile_cover.hpp> @@ -39,50 +39,77 @@ bool TilePyramid::isLoaded() const { return true; } -void TilePyramid::invalidateTiles() { - tiles.clear(); - renderTiles.clear(); - cache.clear(); -} - -void TilePyramid::startRender(const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState& transform) { - for (auto& pair : renderTiles) { - auto& tile = pair.second; - tile.calculateMatrices(projMatrix, clipMatrix, transform); +void TilePyramid::startRender(PaintParameters& parameters) { + for (auto& tile : renderTiles) { + tile.startRender(parameters); } } -void TilePyramid::finishRender(Painter& painter) { - for (auto& pair : renderTiles) { - auto& tile = pair.second; - if (tile.used) { - painter.renderTileDebug(tile); - } +void TilePyramid::finishRender(PaintParameters& parameters) { + for (auto& tile : renderTiles) { + tile.finishRender(parameters); } } -std::map<UnwrappedTileID, RenderTile>& TilePyramid::getRenderTiles() { - return renderTiles; +std::vector<std::reference_wrapper<RenderTile>> TilePyramid::getRenderTiles() { + return { renderTiles.begin(), renderTiles.end() }; } -void TilePyramid::updateTiles(const TileParameters& parameters, - const SourceType type, - const uint16_t tileSize, - const Range<uint8_t> zoomRange, - std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) { +void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layers, + const bool needsRendering, + const bool needsRelayout, + const TileParameters& parameters, + const SourceType type, + const uint16_t tileSize, + const Range<uint8_t> zoomRange, + std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) { + // If we need a relayout, abandon any cached tiles; they're now stale. + if (needsRelayout) { + cache.clear(); + } + + // If we're not going to render anything, move our existing tiles into + // the cache (if they're not stale) or abandon them, and return. + if (!needsRendering) { + if (!needsRelayout) { + for (auto& entry : tiles) { + cache.add(entry.first, std::move(entry.second)); + } + } + + tiles.clear(); + renderTiles.clear(); + + return; + } + // Determine the overzooming/underzooming amounts and required tiles. int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize); int32_t tileZoom = overscaledZoom; + int32_t panZoom = zoomRange.max; std::vector<UnwrappedTileID> idealTiles; + std::vector<UnwrappedTileID> panTiles; + if (overscaledZoom >= zoomRange.min) { int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom); // Make sure we're not reparsing overzoomed raster tiles. if (type == SourceType::Raster) { tileZoom = idealZoom; + + // FIXME: Prefetching is only enabled for raster + // tiles until we fix #7026. + + // Request lower zoom level tiles (if configure to do so) in an attempt + // to show something on the screen faster at the cost of a little of bandwidth. + if (parameters.prefetchZoomDelta) { + panZoom = std::max<int32_t>(tileZoom - parameters.prefetchZoomDelta, zoomRange.min); + } + + if (panZoom < tileZoom) { + panTiles = util::tileCover(parameters.transformState, panZoom); + } } idealTiles = util::tileCover(parameters.transformState, idealZoom); @@ -95,8 +122,13 @@ void TilePyramid::updateTiles(const TileParameters& parameters, std::set<OverscaledTileID> retain; auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void { - retain.emplace(tile.id); - tile.setNecessity(necessity); + if (retain.emplace(tile.id).second) { + tile.setNecessity(necessity); + } + + if (needsRelayout) { + tile.setLayers(layers); + } }; auto getTileFn = [&](const OverscaledTileID& tileID) -> Tile* { auto it = tiles.find(tileID); @@ -108,6 +140,7 @@ void TilePyramid::updateTiles(const TileParameters& parameters, tile = createTile(tileID); if (tile) { tile->setObserver(observer); + tile->setLayers(layers); } } if (!tile) { @@ -116,10 +149,16 @@ void TilePyramid::updateTiles(const TileParameters& parameters, return tiles.emplace(tileID, std::move(tile)).first->second.get(); }; auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) { - renderTiles.emplace(tileID, RenderTile{ tileID, tile }); + renderTiles.emplace_back(tileID, tile); }; renderTiles.clear(); + + if (!panTiles.empty()) { + algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, + [](const UnwrappedTileID&, Tile&) {}, panTiles, zoomRange, panZoom); + } + algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn, idealTiles, zoomRange, tileZoom); @@ -132,54 +171,41 @@ void TilePyramid::updateTiles(const TileParameters& parameters, cache.setSize(conservativeCacheSize); } - removeStaleTiles(retain); - - const PlacementConfig config { parameters.transformState.getAngle(), - parameters.transformState.getPitch(), - parameters.debugOptions & MapDebugOptions::Collision }; - - for (auto& pair : tiles) { - pair.second->setPlacementConfig(config); - } -} - -// Moves all tiles to the cache except for those specified in the retain set. -void TilePyramid::removeStaleTiles(const std::set<OverscaledTileID>& retain) { // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep // and removes items from tiles that don't have the corresponding key in the retain set. - auto tilesIt = tiles.begin(); - auto retainIt = retain.begin(); - while (tilesIt != tiles.end()) { - if (retainIt == retain.end() || tilesIt->first < *retainIt) { - tilesIt->second->setNecessity(Tile::Necessity::Optional); - cache.add(tilesIt->first, std::move(tilesIt->second)); - tiles.erase(tilesIt++); - } else { - if (!(*retainIt < tilesIt->first)) { - ++tilesIt; + { + auto tilesIt = tiles.begin(); + auto retainIt = retain.begin(); + while (tilesIt != tiles.end()) { + if (retainIt == retain.end() || tilesIt->first < *retainIt) { + if (!needsRelayout) { + tilesIt->second->setNecessity(Tile::Necessity::Optional); + cache.add(tilesIt->first, std::move(tilesIt->second)); + } + tiles.erase(tilesIt++); + } else { + if (!(*retainIt < tilesIt->first)) { + ++tilesIt; + } + ++retainIt; } - ++retainIt; } } -} - -void TilePyramid::removeTiles() { - renderTiles.clear(); - if (!tiles.empty()) { - removeStaleTiles({}); - } -} - -void TilePyramid::reloadTiles() { - cache.clear(); for (auto& pair : tiles) { - pair.second->redoLayout(); + const PlacementConfig config { parameters.transformState.getAngle(), + parameters.transformState.getPitch(), + parameters.transformState.getCameraToCenterDistance(), + parameters.transformState.getCameraToTileDistance(pair.first.toUnwrapped()), + parameters.debugOptions & MapDebugOptions::Collision }; + + pair.second->setPlacementConfig(config); } } std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) const { std::unordered_map<std::string, std::vector<Feature>> result; if (renderTiles.empty() || geometry.empty()) { @@ -195,18 +221,14 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry); - - auto sortRenderTiles = [](const RenderTile& a, const RenderTile& b) { + std::vector<std::reference_wrapper<const RenderTile>> sortedTiles{ renderTiles.begin(), + renderTiles.end() }; + std::sort(sortedTiles.begin(), sortedTiles.end(), [](const RenderTile& a, const RenderTile& b) { return std::tie(a.id.canonical.z, a.id.canonical.y, a.id.wrap, a.id.canonical.x) < std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x); - }; - std::vector<std::reference_wrapper<const RenderTile>> sortedTiles; - std::transform(renderTiles.cbegin(), renderTiles.cend(), std::back_inserter(sortedTiles), - [](const auto& pair) { return std::ref(pair.second); }); - std::sort(sortedTiles.begin(), sortedTiles.end(), sortRenderTiles); + }); - for (const auto& renderTileRef : sortedTiles) { - const RenderTile& renderTile = renderTileRef.get(); + for (const RenderTile& renderTile : sortedTiles) { GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { continue; @@ -226,6 +248,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered renderTile.tile.queryRenderedFeatures(result, tileSpaceQueryGeometry, transformState, + layers, options); } diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index b51c5342de..73a8d34c1c 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -5,6 +5,7 @@ #include <mbgl/tile/tile.hpp> #include <mbgl/tile/tile_cache.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/style/layer_impl.hpp> #include <mbgl/util/mat4.hpp> #include <mbgl/util/feature.hpp> @@ -17,9 +18,10 @@ namespace mbgl { -class Painter; +class PaintParameters; class TransformState; class RenderTile; +class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; class TileParameters; @@ -31,34 +33,24 @@ public: bool isLoaded() const; - // Called when the camera has changed. May load new tiles, unload obsolete tiles, or - // trigger re-placement of existing complete tiles. - void updateTiles(const TileParameters&, - SourceType type, - uint16_t tileSize, - Range<uint8_t> zoomRange, - std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile); + void update(const std::vector<Immutable<style::Layer::Impl>>&, + bool needsRendering, + bool needsRelayout, + const TileParameters&, + SourceType type, + uint16_t tileSize, + Range<uint8_t> zoomRange, + std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile); - // Removes all tiles (by putting them into the cache). - void removeTiles(); + void startRender(PaintParameters&); + void finishRender(PaintParameters&); - // Remove all tiles and clear the cache. - void invalidateTiles(); - - // Request that all loaded tiles re-run the layout operation on the existing source - // data with fresh style information. - void reloadTiles(); - - void startRender(const mat4& projMatrix, - const mat4& clipMatrix, - const TransformState&); - void finishRender(Painter&); - - std::map<UnwrappedTileID, RenderTile>& getRenderTiles(); + std::vector<std::reference_wrapper<RenderTile>> getRenderTiles(); std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions& options) const; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const; @@ -71,12 +63,10 @@ public: bool enabled = false; - void removeStaleTiles(const std::set<OverscaledTileID>&); - std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles; TileCache cache; - std::map<UnwrappedTileID, RenderTile> renderTiles; + std::vector<RenderTile> renderTiles; TileObserver* observer = nullptr; }; diff --git a/src/mbgl/renderer/cascade_parameters.hpp b/src/mbgl/renderer/transition_parameters.hpp index 4096cc5a6b..c47aa2e35f 100644 --- a/src/mbgl/renderer/cascade_parameters.hpp +++ b/src/mbgl/renderer/transition_parameters.hpp @@ -1,16 +1,14 @@ #pragma once #include <mbgl/util/chrono.hpp> -#include <mbgl/style/class_dictionary.hpp> #include <mbgl/style/transition_options.hpp> #include <vector> namespace mbgl { -class CascadeParameters { +class TransitionParameters { public: - std::vector<style::ClassID> classes; TimePoint now; style::TransitionOptions transition; }; diff --git a/src/mbgl/renderer/transitioning_property.hpp b/src/mbgl/renderer/transitioning_property.hpp deleted file mode 100644 index c211ccf116..0000000000 --- a/src/mbgl/renderer/transitioning_property.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include <mbgl/style/property_value.hpp> -#include <mbgl/style/data_driven_property_value.hpp> -#include <mbgl/style/transition_options.hpp> -#include <mbgl/util/interpolate.hpp> - -#include <utility> - -namespace mbgl { - -template <class Value> -class TransitioningProperty { -public: - TransitioningProperty() = default; - - TransitioningProperty(Value value_, - TransitioningProperty<Value> prior_, - style::TransitionOptions transition, - TimePoint now) - : begin(now + transition.delay.value_or(Duration::zero())), - end(begin + transition.duration.value_or(Duration::zero())), - value(std::move(value_)) { - if (transition.isDefined()) { - prior = { std::move(prior_) }; - } - } - - template <class Evaluator> - auto evaluate(const Evaluator& evaluator, TimePoint now) { - auto finalValue = value.evaluate(evaluator); - if (!prior) { - // No prior value. - return finalValue; - } else if (now >= end) { - // Transition from prior value is now complete. - prior = {}; - return finalValue; - } else if (value.isDataDriven()) { - // Transitions to data-driven properties are not supported. - // We snap immediately to the data-driven value so that, when we perform layout, - // we see the data-driven function and can use it to populate vertex buffers. - prior = {}; - return finalValue; - } else if (now < begin) { - // Transition hasn't started yet. - return prior->get().evaluate(evaluator, now); - } else { - // Interpolate between recursively-calculated prior value and final. - float t = std::chrono::duration<float>(now - begin) / (end - begin); - return util::interpolate(prior->get().evaluate(evaluator, now), finalValue, - util::DEFAULT_TRANSITION_EASE.solve(t, 0.001)); - } - } - - bool hasTransition() const { - return bool(prior); - } - - bool isUndefined() const { - return value.isUndefined(); - } - - const Value& getValue() const { - return value; - } - -private: - optional<mapbox::util::recursive_wrapper<TransitioningProperty<Value>>> prior; - TimePoint begin; - TimePoint end; - Value value; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp index ae54ac09e7..b54abc050d 100644 --- a/src/mbgl/renderer/update_parameters.hpp +++ b/src/mbgl/renderer/update_parameters.hpp @@ -1,26 +1,43 @@ #pragma once #include <mbgl/map/mode.hpp> -#include <mbgl/map/update.hpp> +#include <mbgl/map/transform_state.hpp> +#include <mbgl/style/light.hpp> +#include <mbgl/style/image.hpp> +#include <mbgl/style/source.hpp> +#include <mbgl/style/layer.hpp> +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/immutable.hpp> + +#include <vector> namespace mbgl { -class TransformState; -class Scheduler; -class FileSource; class AnnotationManager; class UpdateParameters { public: + const bool styleLoaded; const MapMode mode; - const Update updateFlags; const float pixelRatio; const MapDebugOptions debugOptions; const TimePoint timePoint; - const TransformState& transformState; - Scheduler& scheduler; - FileSource& fileSource; + const TransformState transformState; + + const std::string glyphURL; + const bool spriteLoaded; + const style::TransitionOptions transitionOptions; + const Immutable<style::Light::Impl> light; + const Immutable<std::vector<Immutable<style::Image::Impl>>> images; + const Immutable<std::vector<Immutable<style::Source::Impl>>> sources; + const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers; + AnnotationManager& annotationManager; + + const uint8_t prefetchZoomDelta; + + // For still image requests, render requested + const bool stillImageRequest; }; } // namespace mbgl diff --git a/src/mbgl/shaders/circle.cpp b/src/mbgl/shaders/circle.cpp index 2e0c76122c..c14335914b 100644 --- a/src/mbgl/shaders/circle.cpp +++ b/src/mbgl/shaders/circle.cpp @@ -9,7 +9,9 @@ const char* circle::name = "circle"; const char* circle::vertexSource = R"MBGL_SHADER( uniform mat4 u_matrix; uniform bool u_scale_with_map; +uniform bool u_pitch_with_map; uniform vec2 u_extrude_scale; +uniform highp float u_camera_to_center_distance; attribute vec2 a_pos; @@ -22,6 +24,7 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif + #ifndef HAS_UNIFORM_u_radius uniform lowp float a_radius_t; attribute mediump vec2 a_radius; @@ -30,6 +33,7 @@ varying mediump float radius; uniform mediump float u_radius; #endif + #ifndef HAS_UNIFORM_u_blur uniform lowp float a_blur_t; attribute lowp vec2 a_blur; @@ -38,6 +42,7 @@ varying lowp float blur; uniform lowp float u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; @@ -46,6 +51,7 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif + #ifndef HAS_UNIFORM_u_stroke_color uniform lowp float a_stroke_color_t; attribute highp vec4 a_stroke_color; @@ -54,6 +60,7 @@ varying highp vec4 stroke_color; uniform highp vec4 u_stroke_color; #endif + #ifndef HAS_UNIFORM_u_stroke_width uniform lowp float a_stroke_width_t; attribute mediump vec2 a_stroke_width; @@ -62,6 +69,7 @@ varying mediump float stroke_width; uniform mediump float u_stroke_width; #endif + #ifndef HAS_UNIFORM_u_stroke_opacity uniform lowp float a_stroke_opacity_t; attribute lowp vec2 a_stroke_opacity; @@ -70,63 +78,87 @@ varying lowp float stroke_opacity; uniform lowp float u_stroke_opacity; #endif + varying vec3 v_data; void main(void) { - + #ifndef HAS_UNIFORM_u_color color = unpack_mix_vec4(a_color, a_color_t); #else highp vec4 color = u_color; #endif + #ifndef HAS_UNIFORM_u_radius radius = unpack_mix_vec2(a_radius, a_radius_t); #else mediump float radius = u_radius; #endif + #ifndef HAS_UNIFORM_u_blur blur = unpack_mix_vec2(a_blur, a_blur_t); #else lowp float blur = u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + #ifndef HAS_UNIFORM_u_stroke_color stroke_color = unpack_mix_vec4(a_stroke_color, a_stroke_color_t); #else highp vec4 stroke_color = u_stroke_color; #endif + #ifndef HAS_UNIFORM_u_stroke_width stroke_width = unpack_mix_vec2(a_stroke_width, a_stroke_width_t); #else mediump float stroke_width = u_stroke_width; #endif + #ifndef HAS_UNIFORM_u_stroke_opacity stroke_opacity = unpack_mix_vec2(a_stroke_opacity, a_stroke_opacity_t); #else lowp float stroke_opacity = u_stroke_opacity; #endif + // unencode the extrusion vector that we snuck into the a_pos vector vec2 extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0); // multiply a_pos by 0.5, since we had it * 2 in order to sneak // in extrusion data - gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0, 1); - - if (u_scale_with_map) { - gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale; + vec2 circle_center = floor(a_pos * 0.5); + if (u_pitch_with_map) { + vec2 corner_position = circle_center; + if (u_scale_with_map) { + corner_position += extrude * (radius + stroke_width) * u_extrude_scale; + } else { + // Pitching the circle with the map effectively scales it with the map + // To counteract the effect for pitch-scale: viewport, we rescale the + // whole circle based on the pitch scaling effect at its central point + vec4 projected_center = u_matrix * vec4(circle_center, 0, 1); + corner_position += extrude * (radius + stroke_width) * u_extrude_scale * (projected_center.w / u_camera_to_center_distance); + } + + gl_Position = u_matrix * vec4(corner_position, 0, 1); } else { - gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * gl_Position.w; + gl_Position = u_matrix * vec4(circle_center, 0, 1); + + if (u_scale_with_map) { + gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * u_camera_to_center_distance; + } else { + gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * gl_Position.w; + } } // This is a minimum blur distance that serves as a faux-antialiasing for @@ -146,74 +178,88 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif + #ifndef HAS_UNIFORM_u_radius varying mediump float radius; #else uniform mediump float u_radius; #endif + #ifndef HAS_UNIFORM_u_blur varying lowp float blur; #else uniform lowp float u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity varying lowp float opacity; #else uniform lowp float u_opacity; #endif + #ifndef HAS_UNIFORM_u_stroke_color varying highp vec4 stroke_color; #else uniform highp vec4 u_stroke_color; #endif + #ifndef HAS_UNIFORM_u_stroke_width varying mediump float stroke_width; #else uniform mediump float u_stroke_width; #endif + #ifndef HAS_UNIFORM_u_stroke_opacity varying lowp float stroke_opacity; #else uniform lowp float u_stroke_opacity; #endif + varying vec3 v_data; void main() { - + #ifdef HAS_UNIFORM_u_color highp vec4 color = u_color; #endif + #ifdef HAS_UNIFORM_u_radius mediump float radius = u_radius; #endif + #ifdef HAS_UNIFORM_u_blur lowp float blur = u_blur; #endif + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + #ifdef HAS_UNIFORM_u_stroke_color highp vec4 stroke_color = u_stroke_color; #endif + #ifdef HAS_UNIFORM_u_stroke_width mediump float stroke_width = u_stroke_width; #endif + #ifdef HAS_UNIFORM_u_stroke_opacity lowp float stroke_opacity = u_stroke_opacity; #endif + vec2 extrude = v_data.xy; float extrude_length = length(extrude); diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp index 5f733c6a1e..07fa94e338 100644 --- a/src/mbgl/shaders/collision_box.cpp +++ b/src/mbgl/shaders/collision_box.cpp @@ -8,44 +8,74 @@ namespace shaders { const char* collision_box::name = "collision_box"; const char* collision_box::vertexSource = R"MBGL_SHADER( attribute vec2 a_pos; +attribute vec2 a_anchor_pos; attribute vec2 a_extrude; attribute vec2 a_data; uniform mat4 u_matrix; uniform float u_scale; +uniform float u_pitch; +uniform float u_collision_y_stretch; +uniform float u_camera_to_center_distance; varying float v_max_zoom; varying float v_placement_zoom; +varying float v_perspective_zoom_adjust; +varying vec2 v_fade_tex; void main() { - gl_Position = u_matrix * vec4(a_pos + a_extrude / u_scale, 0.0, 1.0); + vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1); + highp float camera_to_anchor_distance = projectedPoint.w; + highp float collision_perspective_ratio = 1.0 + 0.5 * ((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); + + highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch)); + highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); + + gl_Position = u_matrix * vec4(a_pos + a_extrude * collision_perspective_ratio * collision_adjustment / u_scale, 0.0, 1.0); v_max_zoom = a_data.x; v_placement_zoom = a_data.y; + + v_perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0); + v_fade_tex = vec2((v_placement_zoom + v_perspective_zoom_adjust) / 255.0, 0.0); } )MBGL_SHADER"; const char* collision_box::fragmentSource = R"MBGL_SHADER( uniform float u_zoom; +// u_maxzoom is derived from the maximum scale considered by the CollisionTile +// Labels with placement zoom greater than this value will not be placed, +// regardless of perspective effects. uniform float u_maxzoom; +uniform sampler2D u_fadetexture; +// v_max_zoom is a collision-box-specific value that controls when line-following +// collision boxes are used. varying float v_max_zoom; varying float v_placement_zoom; +varying float v_perspective_zoom_adjust; +varying vec2 v_fade_tex; void main() { float alpha = 0.5; + // Green = no collisions, label is showing gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0) * alpha; - if (v_placement_zoom > u_zoom) { + // Red = collision, label hidden + if (texture2D(u_fadetexture, v_fade_tex).a < 1.0) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha; } - if (u_zoom >= v_max_zoom) { + // Faded black = this collision box is not used at this zoom (for curved labels) + if (u_zoom >= v_max_zoom + v_perspective_zoom_adjust) { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) * alpha * 0.25; } + // Faded blue = the placement scale for this label is beyond the CollisionTile + // max scale, so it's impossible for this label to show without collision detection + // being run again (the label's glyphs haven't even been added to the symbol bucket) if (v_placement_zoom >= u_maxzoom) { gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0) * alpha * 0.2; } diff --git a/src/mbgl/shaders/debug.cpp b/src/mbgl/shaders/debug.cpp index d39dcf25be..d18f3be5d1 100644 --- a/src/mbgl/shaders/debug.cpp +++ b/src/mbgl/shaders/debug.cpp @@ -12,7 +12,12 @@ attribute vec2 a_pos; uniform mat4 u_matrix; void main() { - gl_Position = u_matrix * vec4(a_pos, step(32767.0, a_pos.x), 1); + // We are using Int16 for texture position coordinates to give us enough precision for + // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer + // as an arbitrarily high number to preserve adequate precision when rendering. + // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates, + // so math for modifying either is consistent. + gl_Position = u_matrix * vec4(a_pos, step(8192.0, a_pos.x), 1); } )MBGL_SHADER"; diff --git a/src/mbgl/shaders/fill.cpp b/src/mbgl/shaders/fill.cpp index 8f5f304014..3ba00836a2 100644 --- a/src/mbgl/shaders/fill.cpp +++ b/src/mbgl/shaders/fill.cpp @@ -20,6 +20,7 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif + #ifndef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; @@ -28,20 +29,23 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif -void main() { +void main() { + #ifndef HAS_UNIFORM_u_color color = unpack_mix_vec4(a_color, a_color_t); #else highp vec4 color = u_color; #endif + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + gl_Position = u_matrix * vec4(a_pos, 0, 1); } @@ -54,22 +58,26 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif + #ifndef HAS_UNIFORM_u_opacity varying lowp float opacity; #else uniform lowp float u_opacity; #endif -void main() { +void main() { + #ifdef HAS_UNIFORM_u_color highp vec4 color = u_color; #endif + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + gl_FragColor = color * opacity; #ifdef OVERDRAW_INSPECTOR diff --git a/src/mbgl/shaders/fill_extrusion.cpp b/src/mbgl/shaders/fill_extrusion.cpp index ad14e4f32e..817f73391c 100644 --- a/src/mbgl/shaders/fill_extrusion.cpp +++ b/src/mbgl/shaders/fill_extrusion.cpp @@ -27,6 +27,7 @@ varying lowp float base; uniform lowp float u_base; #endif + #ifndef HAS_UNIFORM_u_height uniform lowp float a_height_t; attribute lowp vec2 a_height; @@ -36,6 +37,7 @@ uniform lowp float u_height; #endif + #ifndef HAS_UNIFORM_u_color uniform lowp float a_color_t; attribute highp vec4 a_color; @@ -44,26 +46,30 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif -void main() { +void main() { + #ifndef HAS_UNIFORM_u_base base = unpack_mix_vec2(a_base, a_base_t); #else lowp float base = u_base; #endif + #ifndef HAS_UNIFORM_u_height height = unpack_mix_vec2(a_height, a_height_t); #else lowp float height = u_height; #endif + #ifndef HAS_UNIFORM_u_color color = unpack_mix_vec4(a_color, a_color_t); #else highp vec4 color = u_color; #endif + base = max(0.0, base); height = max(0.0, height); @@ -113,32 +119,38 @@ varying lowp float base; uniform lowp float u_base; #endif + #ifndef HAS_UNIFORM_u_height varying lowp float height; #else uniform lowp float u_height; #endif + #ifndef HAS_UNIFORM_u_color varying highp vec4 color; #else uniform highp vec4 u_color; #endif -void main() { +void main() { + #ifdef HAS_UNIFORM_u_base lowp float base = u_base; #endif + #ifdef HAS_UNIFORM_u_height lowp float height = u_height; #endif + #ifdef HAS_UNIFORM_u_color highp vec4 color = u_color; #endif + gl_FragColor = v_color; #ifdef OVERDRAW_INSPECTOR diff --git a/src/mbgl/shaders/fill_extrusion_pattern.cpp b/src/mbgl/shaders/fill_extrusion_pattern.cpp index 66c24b1bb0..d3e5eef1bf 100644 --- a/src/mbgl/shaders/fill_extrusion_pattern.cpp +++ b/src/mbgl/shaders/fill_extrusion_pattern.cpp @@ -39,6 +39,7 @@ varying lowp float base; uniform lowp float u_base; #endif + #ifndef HAS_UNIFORM_u_height uniform lowp float a_height_t; attribute lowp vec2 a_height; @@ -47,20 +48,23 @@ varying lowp float height; uniform lowp float u_height; #endif -void main() { +void main() { + #ifndef HAS_UNIFORM_u_base base = unpack_mix_vec2(a_base, a_base_t); #else lowp float base = u_base; #endif + #ifndef HAS_UNIFORM_u_height height = unpack_mix_vec2(a_height, a_height_t); #else lowp float height = u_height; #endif + base = max(0.0, base); height = max(0.0, height); @@ -93,6 +97,7 @@ uniform vec2 u_pattern_tl_a; uniform vec2 u_pattern_br_a; uniform vec2 u_pattern_tl_b; uniform vec2 u_pattern_br_b; +uniform vec2 u_texsize; uniform float u_mix; uniform sampler2D u_image; @@ -108,28 +113,32 @@ varying lowp float base; uniform lowp float u_base; #endif + #ifndef HAS_UNIFORM_u_height varying lowp float height; #else uniform lowp float u_height; #endif -void main() { +void main() { + #ifdef HAS_UNIFORM_u_base lowp float base = u_base; #endif + #ifdef HAS_UNIFORM_u_height lowp float height = u_height; #endif + vec2 imagecoord = mod(v_pos_a, 1.0); - vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord); + vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord); vec4 color1 = texture2D(u_image, pos); vec2 imagecoord_b = mod(v_pos_b, 1.0); - vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b); + vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b); vec4 color2 = texture2D(u_image, pos2); vec4 mixedColor = mix(color1, color2, u_mix); diff --git a/src/mbgl/shaders/fill_outline.cpp b/src/mbgl/shaders/fill_outline.cpp index 45bc716be3..9ade598d10 100644 --- a/src/mbgl/shaders/fill_outline.cpp +++ b/src/mbgl/shaders/fill_outline.cpp @@ -23,6 +23,7 @@ varying highp vec4 outline_color; uniform highp vec4 u_outline_color; #endif + #ifndef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; @@ -31,20 +32,23 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif -void main() { +void main() { + #ifndef HAS_UNIFORM_u_outline_color outline_color = unpack_mix_vec4(a_outline_color, a_outline_color_t); #else highp vec4 outline_color = u_outline_color; #endif + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + gl_Position = u_matrix * vec4(a_pos, 0, 1); v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world; } @@ -58,26 +62,30 @@ varying highp vec4 outline_color; uniform highp vec4 u_outline_color; #endif + #ifndef HAS_UNIFORM_u_opacity varying lowp float opacity; #else uniform lowp float u_opacity; #endif + varying vec2 v_pos; void main() { - + #ifdef HAS_UNIFORM_u_outline_color highp vec4 outline_color = u_outline_color; #endif + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + float dist = length(v_pos - gl_FragCoord.xy); - float alpha = smoothstep(1.0, 0.0, dist); + float alpha = 1.0 - smoothstep(0.0, 1.0, dist); gl_FragColor = outline_color * (alpha * opacity); #ifdef OVERDRAW_INSPECTOR diff --git a/src/mbgl/shaders/fill_outline_pattern.cpp b/src/mbgl/shaders/fill_outline_pattern.cpp index 5315709e3a..11cddb7d07 100644 --- a/src/mbgl/shaders/fill_outline_pattern.cpp +++ b/src/mbgl/shaders/fill_outline_pattern.cpp @@ -32,14 +32,16 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif -void main() { +void main() { + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + gl_Position = u_matrix * vec4(a_pos, 0, 1); v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos); @@ -54,6 +56,7 @@ uniform vec2 u_pattern_tl_a; uniform vec2 u_pattern_br_a; uniform vec2 u_pattern_tl_b; uniform vec2 u_pattern_br_b; +uniform vec2 u_texsize; uniform float u_mix; uniform sampler2D u_image; @@ -69,24 +72,26 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif -void main() { +void main() { + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + vec2 imagecoord = mod(v_pos_a, 1.0); - vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord); + vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord); vec4 color1 = texture2D(u_image, pos); vec2 imagecoord_b = mod(v_pos_b, 1.0); - vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b); + vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b); vec4 color2 = texture2D(u_image, pos2); // find distance to outline for alpha interpolation float dist = length(v_pos - gl_FragCoord.xy); - float alpha = smoothstep(1.0, 0.0, dist); + float alpha = 1.0 - smoothstep(0.0, 1.0, dist); gl_FragColor = mix(color1, color2, u_mix) * alpha * opacity; diff --git a/src/mbgl/shaders/fill_pattern.cpp b/src/mbgl/shaders/fill_pattern.cpp index dd99e4efff..a3817c4426 100644 --- a/src/mbgl/shaders/fill_pattern.cpp +++ b/src/mbgl/shaders/fill_pattern.cpp @@ -30,14 +30,16 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif -void main() { +void main() { + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + gl_Position = u_matrix * vec4(a_pos, 0, 1); v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos); @@ -50,6 +52,7 @@ uniform vec2 u_pattern_tl_a; uniform vec2 u_pattern_br_a; uniform vec2 u_pattern_tl_b; uniform vec2 u_pattern_br_b; +uniform vec2 u_texsize; uniform float u_mix; uniform sampler2D u_image; @@ -64,18 +67,20 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif -void main() { +void main() { + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + vec2 imagecoord = mod(v_pos_a, 1.0); - vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord); + vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord); vec4 color1 = texture2D(u_image, pos); vec2 imagecoord_b = mod(v_pos_b, 1.0); - vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b); + vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b); vec4 color2 = texture2D(u_image, pos2); gl_FragColor = mix(color1, color2, u_mix) * opacity; diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp index aa59181fb4..c700295a15 100644 --- a/src/mbgl/shaders/line.cpp +++ b/src/mbgl/shaders/line.cpp @@ -26,7 +26,6 @@ attribute vec4 a_data; uniform mat4 u_matrix; uniform mediump float u_ratio; -uniform mediump float u_width; uniform vec2 u_gl_units_to_pixels; varying vec2 v_normal; @@ -42,6 +41,7 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif + #ifndef HAS_UNIFORM_u_blur uniform lowp float a_blur_t; attribute lowp vec2 a_blur; @@ -50,6 +50,7 @@ varying lowp float blur; uniform lowp float u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; @@ -58,6 +59,7 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif + #ifndef HAS_UNIFORM_u_gapwidth uniform lowp float a_gapwidth_t; attribute mediump vec2 a_gapwidth; @@ -65,6 +67,7 @@ attribute mediump vec2 a_gapwidth; uniform mediump float u_gapwidth; #endif + #ifndef HAS_UNIFORM_u_offset uniform lowp float a_offset_t; attribute lowp vec2 a_offset; @@ -72,38 +75,59 @@ attribute lowp vec2 a_offset; uniform lowp float u_offset; #endif -void main() { +#ifndef HAS_UNIFORM_u_width +uniform lowp float a_width_t; +attribute mediump vec2 a_width; +#else +uniform mediump float u_width; +#endif + + +void main() { + #ifndef HAS_UNIFORM_u_color color = unpack_mix_vec4(a_color, a_color_t); #else highp vec4 color = u_color; #endif + #ifndef HAS_UNIFORM_u_blur blur = unpack_mix_vec2(a_blur, a_blur_t); #else lowp float blur = u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + #ifndef HAS_UNIFORM_u_gapwidth mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t); #else mediump float gapwidth = u_gapwidth; #endif + #ifndef HAS_UNIFORM_u_offset lowp float offset = unpack_mix_vec2(a_offset, a_offset_t); #else lowp float offset = u_offset; #endif + +#ifndef HAS_UNIFORM_u_width + mediump float width = unpack_mix_vec2(a_width, a_width_t); +#else + mediump float width = u_width; +#endif + + vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; @@ -117,11 +141,11 @@ void main() { // these transformations used to be applied in the JS and native code bases. // moved them into the shader for clarity and simplicity. gapwidth = gapwidth / 2.0; - float width = u_width / 2.0; + float halfwidth = width / 2.0; offset = -1.0 * offset; float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); - float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; + float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; // Scale the extrusion vector down to a normal and then up by the line width // of this vertex. @@ -155,36 +179,42 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif + #ifndef HAS_UNIFORM_u_blur varying lowp float blur; #else uniform lowp float u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity varying lowp float opacity; #else uniform lowp float u_opacity; #endif + varying vec2 v_width2; varying vec2 v_normal; varying float v_gamma_scale; void main() { - + #ifdef HAS_UNIFORM_u_color highp vec4 color = u_color; #endif + #ifdef HAS_UNIFORM_u_blur lowp float blur = u_blur; #endif + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + // Calculate the distance of the pixel from the line in pixels. float dist = length(v_normal) * v_width2.s; diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp index d46858aa9e..f8d785ade9 100644 --- a/src/mbgl/shaders/line_pattern.cpp +++ b/src/mbgl/shaders/line_pattern.cpp @@ -28,7 +28,6 @@ attribute vec4 a_data; uniform mat4 u_matrix; uniform mediump float u_ratio; -uniform mediump float u_width; uniform vec2 u_gl_units_to_pixels; varying vec2 v_normal; @@ -45,6 +44,7 @@ varying lowp float blur; uniform lowp float u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; @@ -53,6 +53,7 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif + #ifndef HAS_UNIFORM_u_offset uniform lowp float a_offset_t; attribute lowp vec2 a_offset; @@ -60,6 +61,7 @@ attribute lowp vec2 a_offset; uniform lowp float u_offset; #endif + #ifndef HAS_UNIFORM_u_gapwidth uniform lowp float a_gapwidth_t; attribute mediump vec2 a_gapwidth; @@ -67,32 +69,52 @@ attribute mediump vec2 a_gapwidth; uniform mediump float u_gapwidth; #endif -void main() { +#ifndef HAS_UNIFORM_u_width +uniform lowp float a_width_t; +attribute mediump vec2 a_width; +#else +uniform mediump float u_width; +#endif + + +void main() { + #ifndef HAS_UNIFORM_u_blur blur = unpack_mix_vec2(a_blur, a_blur_t); #else lowp float blur = u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + #ifndef HAS_UNIFORM_u_offset lowp float offset = unpack_mix_vec2(a_offset, a_offset_t); #else lowp float offset = u_offset; #endif + #ifndef HAS_UNIFORM_u_gapwidth mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t); #else mediump float gapwidth = u_gapwidth; #endif + +#ifndef HAS_UNIFORM_u_width + mediump float width = unpack_mix_vec2(a_width, a_width_t); +#else + mediump float width = u_width; +#endif + + vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE; @@ -107,11 +129,11 @@ void main() { // these transformations used to be applied in the JS and native code bases. // moved them into the shader for clarity and simplicity. gapwidth = gapwidth / 2.0; - float width = u_width / 2.0; + float halfwidth = width / 2.0; offset = -1.0 * offset; float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); - float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; + float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; // Scale the extrusion vector down to a normal and then up by the line width // of this vertex. @@ -145,6 +167,7 @@ uniform vec2 u_pattern_tl_a; uniform vec2 u_pattern_br_a; uniform vec2 u_pattern_tl_b; uniform vec2 u_pattern_br_b; +uniform vec2 u_texsize; uniform float u_fade; uniform sampler2D u_image; @@ -161,22 +184,26 @@ varying lowp float blur; uniform lowp float u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity varying lowp float opacity; #else uniform lowp float u_opacity; #endif -void main() { +void main() { + #ifdef HAS_UNIFORM_u_blur lowp float blur = u_blur; #endif + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + // Calculate the distance of the pixel from the line in pixels. float dist = length(v_normal) * v_width2.s; @@ -190,8 +217,8 @@ void main() { float x_b = mod(v_linesofar / u_pattern_size_b.x, 1.0); float y_a = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_a.y); float y_b = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_b.y); - vec2 pos_a = mix(u_pattern_tl_a, u_pattern_br_a, vec2(x_a, y_a)); - vec2 pos_b = mix(u_pattern_tl_b, u_pattern_br_b, vec2(x_b, y_b)); + vec2 pos_a = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, vec2(x_a, y_a)); + vec2 pos_b = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, vec2(x_b, y_b)); vec4 color = mix(texture2D(u_image, pos_a), texture2D(u_image, pos_b), u_fade); diff --git a/src/mbgl/shaders/line_sdf.cpp b/src/mbgl/shaders/line_sdf.cpp index fb6046d8a5..c5d50566e8 100644 --- a/src/mbgl/shaders/line_sdf.cpp +++ b/src/mbgl/shaders/line_sdf.cpp @@ -33,7 +33,6 @@ uniform float u_tex_y_a; uniform vec2 u_patternscale_b; uniform float u_tex_y_b; uniform vec2 u_gl_units_to_pixels; -uniform mediump float u_width; varying vec2 v_normal; varying vec2 v_width2; @@ -50,6 +49,7 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif + #ifndef HAS_UNIFORM_u_blur uniform lowp float a_blur_t; attribute lowp vec2 a_blur; @@ -58,6 +58,7 @@ varying lowp float blur; uniform lowp float u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; @@ -66,6 +67,7 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif + #ifndef HAS_UNIFORM_u_gapwidth uniform lowp float a_gapwidth_t; attribute mediump vec2 a_gapwidth; @@ -73,6 +75,7 @@ attribute mediump vec2 a_gapwidth; uniform mediump float u_gapwidth; #endif + #ifndef HAS_UNIFORM_u_offset uniform lowp float a_offset_t; attribute lowp vec2 a_offset; @@ -80,38 +83,76 @@ attribute lowp vec2 a_offset; uniform lowp float u_offset; #endif -void main() { +#ifndef HAS_UNIFORM_u_width +uniform lowp float a_width_t; +attribute mediump vec2 a_width; +varying mediump float width; +#else +uniform mediump float u_width; +#endif + + +#ifndef HAS_UNIFORM_u_floorwidth +uniform lowp float a_floorwidth_t; +attribute lowp vec2 a_floorwidth; +varying lowp float floorwidth; +#else +uniform lowp float u_floorwidth; +#endif + + +void main() { + #ifndef HAS_UNIFORM_u_color color = unpack_mix_vec4(a_color, a_color_t); #else highp vec4 color = u_color; #endif + #ifndef HAS_UNIFORM_u_blur blur = unpack_mix_vec2(a_blur, a_blur_t); #else lowp float blur = u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + #ifndef HAS_UNIFORM_u_gapwidth mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t); #else mediump float gapwidth = u_gapwidth; #endif + #ifndef HAS_UNIFORM_u_offset lowp float offset = unpack_mix_vec2(a_offset, a_offset_t); #else lowp float offset = u_offset; #endif + +#ifndef HAS_UNIFORM_u_width + width = unpack_mix_vec2(a_width, a_width_t); +#else + mediump float width = u_width; +#endif + + +#ifndef HAS_UNIFORM_u_floorwidth + floorwidth = unpack_mix_vec2(a_floorwidth, a_floorwidth_t); +#else + lowp float floorwidth = u_floorwidth; +#endif + + vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE; @@ -126,11 +167,11 @@ void main() { // these transformations used to be applied in the JS and native code bases. // moved them into the shader for clarity and simplicity. gapwidth = gapwidth / 2.0; - float width = u_width / 2.0; + float halfwidth = width / 2.0; offset = -1.0 * offset; float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); - float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; + float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; // Scale the extrusion vector down to a normal and then up by the line width // of this vertex. @@ -152,8 +193,8 @@ void main() { float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels); v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; - v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a); - v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b); + v_tex_a = vec2(a_linesofar * u_patternscale_a.x / floorwidth, normal.y * u_patternscale_a.y + u_tex_y_a); + v_tex_b = vec2(a_linesofar * u_patternscale_b.x / floorwidth, normal.y * u_patternscale_b.y + u_tex_y_b); v_width2 = vec2(outset, inset); } @@ -178,32 +219,62 @@ varying highp vec4 color; uniform highp vec4 u_color; #endif + #ifndef HAS_UNIFORM_u_blur varying lowp float blur; #else uniform lowp float u_blur; #endif + #ifndef HAS_UNIFORM_u_opacity varying lowp float opacity; #else uniform lowp float u_opacity; #endif -void main() { +#ifndef HAS_UNIFORM_u_width +varying mediump float width; +#else +uniform mediump float u_width; +#endif + + +#ifndef HAS_UNIFORM_u_floorwidth +varying lowp float floorwidth; +#else +uniform lowp float u_floorwidth; +#endif + + +void main() { + #ifdef HAS_UNIFORM_u_color highp vec4 color = u_color; #endif + #ifdef HAS_UNIFORM_u_blur lowp float blur = u_blur; #endif + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + +#ifdef HAS_UNIFORM_u_width + mediump float width = u_width; +#endif + + +#ifdef HAS_UNIFORM_u_floorwidth + lowp float floorwidth = u_floorwidth; +#endif + + // Calculate the distance of the pixel from the line in pixels. float dist = length(v_normal) * v_width2.s; @@ -216,7 +287,7 @@ void main() { float sdfdist_a = texture2D(u_image, v_tex_a).a; float sdfdist_b = texture2D(u_image, v_tex_b).a; float sdfdist = mix(sdfdist_a, sdfdist_b, u_mix); - alpha *= smoothstep(0.5 - u_sdfgamma, 0.5 + u_sdfgamma, sdfdist); + alpha *= smoothstep(0.5 - u_sdfgamma / floorwidth, 0.5 + u_sdfgamma / floorwidth, sdfdist); gl_FragColor = color * (alpha * opacity); diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp index 95fa624e8d..feb185a684 100644 --- a/src/mbgl/shaders/preludes.cpp +++ b/src/mbgl/shaders/preludes.cpp @@ -24,25 +24,6 @@ precision highp float; #endif -float evaluate_zoom_function_1(const vec4 values, const float t) { - if (t < 1.0) { - return mix(values[0], values[1], t); - } else if (t < 2.0) { - return mix(values[1], values[2], t - 1.0); - } else { - return mix(values[2], values[3], t - 2.0); - } -} -vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { - if (t < 1.0) { - return mix(value0, value1, t); - } else if (t < 2.0) { - return mix(value1, value2, t - 1.0); - } else { - return mix(value2, value3, t - 2.0); - } -} - // Unpack a pair of values that have been packed into a single float. // The packed values are assumed to be 8-bit unsigned integers, and are // packed like so: @@ -54,8 +35,8 @@ vec2 unpack_float(const float packedValue) { } -// To minimize the number of attributes needed in the mapbox-gl-native shaders, -// we encode a 4-component color into a pair of floats (i.e. a vec2) as follows: +// To minimize the number of attributes needed, we encode a 4-component +// color into a pair of floats (i.e. a vec2) as follows: // [ floor(color.r * 255) * 256 + color.g * 255, // floor(color.b * 255) * 256 + color.g * 255 ] vec4 decode_color(const vec2 encodedColor) { diff --git a/src/mbgl/shaders/raster.cpp b/src/mbgl/shaders/raster.cpp index eb7a2db240..98291bfec6 100644 --- a/src/mbgl/shaders/raster.cpp +++ b/src/mbgl/shaders/raster.cpp @@ -20,7 +20,12 @@ varying vec2 v_pos1; void main() { gl_Position = u_matrix * vec4(a_pos, 0, 1); - v_pos0 = (((a_texture_pos / 32767.0) - 0.5) / u_buffer_scale ) + 0.5; + // We are using Int16 for texture position coordinates to give us enough precision for + // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer + // as an arbitrarily high number to preserve adequate precision when rendering. + // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates, + // so math for modifying either is consistent. + v_pos0 = (((a_texture_pos / 8192.0) - 0.5) / u_buffer_scale ) + 0.5; v_pos1 = (v_pos0 * u_scale_parent) + u_tl_parent; } @@ -45,6 +50,12 @@ void main() { // read and cross-fade colors from the main and parent tiles vec4 color0 = texture2D(u_image0, v_pos0); vec4 color1 = texture2D(u_image1, v_pos1); + if (color0.a > 0.0) { + color0.rgb = color0.rgb / color0.a; + } + if (color1.a > 0.0) { + color1.rgb = color1.rgb / color1.a; + } vec4 color = mix(color0, color1, u_fade_t); color.a *= u_opacity; vec3 rgb = color.rgb; diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index bc570cf361..1e96194738 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -7,17 +7,21 @@ namespace shaders { const char* symbol_icon::name = "symbol_icon"; const char* symbol_icon::vertexSource = R"MBGL_SHADER( +const float PI = 3.141592653589793; attribute vec4 a_pos_offset; attribute vec4 a_data; +attribute vec3 a_projected_pos; -// icon-size data (see symbol_sdf.vertex.glsl for more) -attribute vec3 a_size; uniform bool u_is_size_zoom_constant; uniform bool u_is_size_feature_constant; -uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function -uniform mediump float u_size; // used when size is both zoom and feature constant -uniform mediump float u_layout_size; // used when size is feature constant +uniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function +uniform highp float u_size; // used when size is both zoom and feature constant +uniform highp float u_camera_to_center_distance; +uniform highp float u_pitch; +uniform bool u_rotate_symbol; +uniform highp float u_aspect_ratio; +uniform highp float u_collision_y_stretch; #ifndef HAS_UNIFORM_u_opacity @@ -28,13 +32,13 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif -// matrix is for the vertex position. + uniform mat4 u_matrix; +uniform mat4 u_label_plane_matrix; +uniform mat4 u_gl_coord_matrix; uniform bool u_is_text; -uniform mediump float u_zoom; -uniform bool u_rotate_with_map; -uniform vec2 u_extrude_scale; +uniform bool u_pitch_with_map; uniform vec2 u_texsize; @@ -42,64 +46,73 @@ varying vec2 v_tex; varying vec2 v_fade_tex; void main() { - + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + vec2 a_pos = a_pos_offset.xy; vec2 a_offset = a_pos_offset.zw; vec2 a_tex = a_data.xy; - mediump vec2 label_data = unpack_float(a_data[2]); - mediump float a_labelminzoom = label_data[0]; - mediump vec2 a_zoom = unpack_float(a_data[3]); - mediump float a_minzoom = a_zoom[0]; - mediump float a_maxzoom = a_zoom[1]; + vec2 a_size = a_data.zw; + + highp vec2 angle_labelminzoom = unpack_float(a_projected_pos[2]); + highp float segment_angle = -angle_labelminzoom[0] / 255.0 * 2.0 * PI; + mediump float a_labelminzoom = angle_labelminzoom[1]; float size; - // In order to accommodate placing labels around corners in - // symbol-placement: line, each glyph in a label could have multiple - // "quad"s only one of which should be shown at a given zoom level. - // The min/max zoom assigned to each quad is based on the font size at - // the vector tile's zoom level, which might be different than at the - // currently rendered zoom level if text-size is zoom-dependent. - // Thus, we compensate for this difference by calculating an adjustment - // based on the scale of rendered text size relative to layout text size. - mediump float layoutSize; if (!u_is_size_zoom_constant && !u_is_size_feature_constant) { size = mix(a_size[0], a_size[1], u_size_t) / 10.0; - layoutSize = a_size[2] / 10.0; } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) { size = a_size[0] / 10.0; - layoutSize = size; } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) { size = u_size; - layoutSize = u_layout_size; } else { size = u_size; - layoutSize = u_size; } + vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1); + highp float camera_to_anchor_distance = projectedPoint.w; + // See comments in symbol_sdf.vertex + highp float distance_ratio = u_pitch_with_map ? + camera_to_anchor_distance / u_camera_to_center_distance : + u_camera_to_center_distance / camera_to_anchor_distance; + highp float perspective_ratio = 0.5 + 0.5 * distance_ratio; + + size *= perspective_ratio; + float fontScale = u_is_text ? size / 24.0 : size; - mediump float zoomAdjust = log2(size / layoutSize); - mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0; - // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise - mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom)); + highp float symbol_rotation = 0.0; + if (u_rotate_symbol) { + // See comments in symbol_sdf.vertex + vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1); - vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0); - if (u_rotate_with_map) { - gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); - gl_Position.z += z * gl_Position.w; - } else { - gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); + vec2 a = projectedPoint.xy / projectedPoint.w; + vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w; + + symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x); } + highp float angle_sin = sin(segment_angle + symbol_rotation); + highp float angle_cos = cos(segment_angle + symbol_rotation); + mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); + + vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); + gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0); + v_tex = a_tex / u_texsize; - v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0); + // See comments in symbol_sdf.vertex + highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch)); + highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); + + highp float collision_perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); + highp float perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0); + v_fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0); } )MBGL_SHADER"; @@ -114,15 +127,17 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif + varying vec2 v_tex; varying vec2 v_fade_tex; void main() { - + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity; gl_FragColor = texture2D(u_texture, v_tex) * alpha; diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index cce6b769a6..a4427f31ab 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -11,6 +11,7 @@ const float PI = 3.141592653589793; attribute vec4 a_pos_offset; attribute vec4 a_data; +attribute vec3 a_projected_pos; // contents of a_size vary based on the type of property value // used for {text,icon}-size. @@ -18,14 +19,11 @@ attribute vec4 a_data; // For source functions, we bind only one value per vertex: the value of {text,icon}-size evaluated for the current feature. // For composite functions: // [ text-size(lowerZoomStop, feature), -// text-size(upperZoomStop, feature), -// layoutSize == text-size(layoutZoomLevel, feature) ] -attribute vec3 a_size; +// text-size(upperZoomStop, feature) ] uniform bool u_is_size_zoom_constant; uniform bool u_is_size_feature_constant; -uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function -uniform mediump float u_size; // used when size is both zoom and feature constant -uniform mediump float u_layout_size; // used when size is feature constant +uniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function +uniform highp float u_size; // used when size is both zoom and feature constant #ifndef HAS_UNIFORM_u_fill_color @@ -36,6 +34,7 @@ varying highp vec4 fill_color; uniform highp vec4 u_fill_color; #endif + #ifndef HAS_UNIFORM_u_halo_color uniform lowp float a_halo_color_t; attribute highp vec4 a_halo_color; @@ -44,6 +43,7 @@ varying highp vec4 halo_color; uniform highp vec4 u_halo_color; #endif + #ifndef HAS_UNIFORM_u_opacity uniform lowp float a_opacity_t; attribute lowp vec2 a_opacity; @@ -52,6 +52,7 @@ varying lowp float opacity; uniform lowp float u_opacity; #endif + #ifndef HAS_UNIFORM_u_halo_width uniform lowp float a_halo_width_t; attribute lowp vec2 a_halo_width; @@ -60,6 +61,7 @@ varying lowp float halo_width; uniform lowp float u_halo_width; #endif + #ifndef HAS_UNIFORM_u_halo_blur uniform lowp float a_halo_blur_t; attribute lowp vec2 a_halo_blur; @@ -68,17 +70,18 @@ varying lowp float halo_blur; uniform lowp float u_halo_blur; #endif -// matrix is for the vertex position. + uniform mat4 u_matrix; +uniform mat4 u_label_plane_matrix; +uniform mat4 u_gl_coord_matrix; uniform bool u_is_text; -uniform mediump float u_zoom; -uniform bool u_rotate_with_map; uniform bool u_pitch_with_map; -uniform mediump float u_pitch; -uniform mediump float u_bearing; -uniform mediump float u_aspect_ratio; -uniform vec2 u_extrude_scale; +uniform highp float u_pitch; +uniform bool u_rotate_symbol; +uniform highp float u_aspect_ratio; +uniform highp float u_camera_to_center_distance; +uniform highp float u_collision_y_stretch; uniform vec2 u_texsize; @@ -86,129 +89,124 @@ varying vec4 v_data0; varying vec2 v_data1; void main() { - + #ifndef HAS_UNIFORM_u_fill_color fill_color = unpack_mix_vec4(a_fill_color, a_fill_color_t); #else highp vec4 fill_color = u_fill_color; #endif + #ifndef HAS_UNIFORM_u_halo_color halo_color = unpack_mix_vec4(a_halo_color, a_halo_color_t); #else highp vec4 halo_color = u_halo_color; #endif + #ifndef HAS_UNIFORM_u_opacity opacity = unpack_mix_vec2(a_opacity, a_opacity_t); #else lowp float opacity = u_opacity; #endif + #ifndef HAS_UNIFORM_u_halo_width halo_width = unpack_mix_vec2(a_halo_width, a_halo_width_t); #else lowp float halo_width = u_halo_width; #endif + #ifndef HAS_UNIFORM_u_halo_blur halo_blur = unpack_mix_vec2(a_halo_blur, a_halo_blur_t); #else lowp float halo_blur = u_halo_blur; #endif + vec2 a_pos = a_pos_offset.xy; vec2 a_offset = a_pos_offset.zw; vec2 a_tex = a_data.xy; + vec2 a_size = a_data.zw; - mediump vec2 label_data = unpack_float(a_data[2]); - mediump float a_labelminzoom = label_data[0]; - mediump float a_labelangle = label_data[1]; - - mediump vec2 a_zoom = unpack_float(a_data[3]); - mediump float a_minzoom = a_zoom[0]; - mediump float a_maxzoom = a_zoom[1]; + highp vec2 angle_labelminzoom = unpack_float(a_projected_pos[2]); + highp float segment_angle = -angle_labelminzoom[0] / 255.0 * 2.0 * PI; + mediump float a_labelminzoom = angle_labelminzoom[1]; float size; - // In order to accommodate placing labels around corners in - // symbol-placement: line, each glyph in a label could have multiple - // "quad"s only one of which should be shown at a given zoom level. - // The min/max zoom assigned to each quad is based on the font size at - // the vector tile's zoom level, which might be different than at the - // currently rendered zoom level if text-size is zoom-dependent. - // Thus, we compensate for this difference by calculating an adjustment - // based on the scale of rendered text size relative to layout text size. - mediump float layoutSize; if (!u_is_size_zoom_constant && !u_is_size_feature_constant) { size = mix(a_size[0], a_size[1], u_size_t) / 10.0; - layoutSize = a_size[2] / 10.0; } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) { size = a_size[0] / 10.0; - layoutSize = size; } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) { size = u_size; - layoutSize = u_layout_size; } else { size = u_size; - layoutSize = u_size; } + vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1); + highp float camera_to_anchor_distance = projectedPoint.w; + // If the label is pitched with the map, layout is done in pitched space, + // which makes labels in the distance smaller relative to viewport space. + // We counteract part of that effect by multiplying by the perspective ratio. + // If the label isn't pitched with the map, we do layout in viewport space, + // which makes labels in the distance larger relative to the features around + // them. We counteract part of that effect by dividing by the perspective ratio. + highp float distance_ratio = u_pitch_with_map ? + camera_to_anchor_distance / u_camera_to_center_distance : + u_camera_to_center_distance / camera_to_anchor_distance; + highp float perspective_ratio = 0.5 + 0.5 * distance_ratio; + + size *= perspective_ratio; + float fontScale = u_is_text ? size / 24.0 : size; - mediump float zoomAdjust = log2(size / layoutSize); - mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0; - // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise - // Used below to move the vertex out of the clip space for when the current - // zoom is out of the glyph's zoom range. - mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom)); - - // pitch-alignment: map - // rotation-alignment: map | viewport - if (u_pitch_with_map) { - lowp float angle = u_rotate_with_map ? (a_labelangle / 256.0 * 2.0 * PI) : u_bearing; - lowp float asin = sin(angle); - lowp float acos = cos(angle); - mat2 RotationMatrix = mat2(acos, asin, -1.0 * asin, acos); - vec2 offset = RotationMatrix * a_offset; - vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0); - gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); - gl_Position.z += z * gl_Position.w; - // pitch-alignment: viewport - // rotation-alignment: map - } else if (u_rotate_with_map) { - // foreshortening factor to apply on pitched maps - // as a label goes from horizontal <=> vertical in angle - // it goes from 0% foreshortening to up to around 70% foreshortening - lowp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75)); - - lowp float lineangle = a_labelangle / 256.0 * 2.0 * PI; - - // use the lineangle to position points a,b along the line - // project the points and calculate the label angle in projected space - // this calculation allows labels to be rendered unskewed on pitched maps - vec4 a = u_matrix * vec4(a_pos, 0, 1); - vec4 b = u_matrix * vec4(a_pos + vec2(cos(lineangle),sin(lineangle)), 0, 1); - lowp float angle = atan((b[1]/b[3] - a[1]/a[3])/u_aspect_ratio, b[0]/b[3] - a[0]/a[3]); - lowp float asin = sin(angle); - lowp float acos = cos(angle); - mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos); - - vec2 offset = RotationMatrix * (vec2((1.0-pitchfactor)+(pitchfactor*cos(angle*2.0)), 1.0) * a_offset); - vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); - gl_Position.z += z * gl_Position.w; - // pitch-alignment: viewport - // rotation-alignment: viewport - } else { - vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); + highp float symbol_rotation = 0.0; + if (u_rotate_symbol) { + // Point labels with 'rotation-alignment: map' are horizontal with respect to tile units + // To figure out that angle in projected space, we draw a short horizontal line in tile + // space, project it, and measure its angle in projected space. + vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1); + + vec2 a = projectedPoint.xy / projectedPoint.w; + vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w; + + symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x); } + highp float angle_sin = sin(segment_angle + symbol_rotation); + highp float angle_cos = cos(segment_angle + symbol_rotation); + mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); + + vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); + gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0); float gamma_scale = gl_Position.w; vec2 tex = a_tex / u_texsize; - vec2 fade_tex = vec2(a_labelminzoom / 255.0, 0.0); + // incidence_stretch is the ratio of how much y space a label takes up on a tile while drawn perpendicular to the viewport vs + // how much space it would take up if it were drawn flat on the tile + // Using law of sines, camera_to_anchor/sin(ground_angle) = camera_to_center/sin(incidence_angle) + // sin(incidence_angle) = 1/incidence_stretch + // Incidence angle 90 -> head on, sin(incidence_angle) = 1, no incidence stretch + // Incidence angle 1 -> very oblique, sin(incidence_angle) =~ 0, lots of incidence stretch + // ground_angle = u_pitch + PI/2 -> sin(ground_angle) = cos(u_pitch) + // This 2D calculation is only exactly correct when gl_Position.x is in the center of the viewport, + // but it's a close enough approximation for our purposes + highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch)); + // incidence_stretch only applies to the y-axis, but without re-calculating the collision tile, we can't + // adjust the size of only one axis. So, we do a crude approximation at placement time to get the aspect ratio + // about right, and then do the rest of the adjustment here: there will be some extra padding on the x-axis, + // but hopefully not too much. + // Never make the adjustment less than 1.0: instead of allowing collisions on the x-axis, be conservative on + // the y-axis. + highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch); + + // Floor to 1/10th zoom to dodge precision issues that can cause partially hidden labels + highp float collision_perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0); + highp float perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0); + vec2 fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0); v_data0 = vec4(tex.x, tex.y, fade_tex.x, fade_tex.y); v_data1 = vec2(gamma_scale, size); @@ -227,30 +225,35 @@ varying highp vec4 fill_color; uniform highp vec4 u_fill_color; #endif + #ifndef HAS_UNIFORM_u_halo_color varying highp vec4 halo_color; #else uniform highp vec4 u_halo_color; #endif + #ifndef HAS_UNIFORM_u_opacity varying lowp float opacity; #else uniform lowp float u_opacity; #endif + #ifndef HAS_UNIFORM_u_halo_width varying lowp float halo_width; #else uniform lowp float u_halo_width; #endif + #ifndef HAS_UNIFORM_u_halo_blur varying lowp float halo_blur; #else uniform lowp float u_halo_blur; #endif + uniform sampler2D u_texture; uniform sampler2D u_fadetexture; uniform highp float u_gamma_scale; @@ -260,27 +263,32 @@ varying vec4 v_data0; varying vec2 v_data1; void main() { - + #ifdef HAS_UNIFORM_u_fill_color highp vec4 fill_color = u_fill_color; #endif + #ifdef HAS_UNIFORM_u_halo_color highp vec4 halo_color = u_halo_color; #endif + #ifdef HAS_UNIFORM_u_opacity lowp float opacity = u_opacity; #endif + #ifdef HAS_UNIFORM_u_halo_width lowp float halo_width = u_halo_width; #endif + #ifdef HAS_UNIFORM_u_halo_blur lowp float halo_blur = u_halo_blur; #endif + vec2 tex = v_data0.xy; vec2 fade_tex = v_data0.zw; float gamma_scale = v_data1.x; diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp deleted file mode 100644 index fed8451aed..0000000000 --- a/src/mbgl/sprite/sprite_atlas.cpp +++ /dev/null @@ -1,340 +0,0 @@ -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/sprite/sprite_atlas_worker.hpp> -#include <mbgl/sprite/sprite_atlas_observer.hpp> -#include <mbgl/sprite/sprite_parser.hpp> -#include <mbgl/gl/context.hpp> -#include <mbgl/util/logging.hpp> -#include <mbgl/util/platform.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/std.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/exception.hpp> -#include <mbgl/storage/file_source.hpp> -#include <mbgl/storage/resource.hpp> -#include <mbgl/storage/response.hpp> -#include <mbgl/util/run_loop.hpp> -#include <mbgl/actor/actor.hpp> - -#include <cassert> -#include <cmath> -#include <algorithm> - -namespace mbgl { - -static SpriteAtlasObserver nullObserver; - -struct SpriteAtlas::Loader { - Loader(Scheduler& scheduler, SpriteAtlas& spriteAtlas) - : mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())), - worker(scheduler, ActorRef<SpriteAtlas>(spriteAtlas, mailbox)) { - } - - std::shared_ptr<const std::string> image; - std::shared_ptr<const std::string> json; - std::unique_ptr<AsyncRequest> jsonRequest; - std::unique_ptr<AsyncRequest> spriteRequest; - std::shared_ptr<Mailbox> mailbox; - Actor<SpriteAtlasWorker> worker; -}; - -SpriteAtlasElement::SpriteAtlasElement(Rect<uint16_t> rect_, - const style::Image& image, - Size size_, float pixelRatio) - : pos(std::move(rect_)), - sdf(image.sdf), - relativePixelRatio(image.pixelRatio / pixelRatio), - width(image.getWidth()), - height(image.getHeight()) { - - const float padding = 1; - - const float w = image.getWidth() * relativePixelRatio; - const float h = image.getHeight() * relativePixelRatio; - - size = {{ float(image.getWidth()), image.getHeight() }}; - tl = {{ float(pos.x + padding) / size_.width, float(pos.y + padding) / size_.height }}; - br = {{ float(pos.x + padding + w) / size_.width, float(pos.y + padding + h) / size_.height }}; -} - -SpriteAtlas::SpriteAtlas(Size size_, float pixelRatio_) - : size(std::move(size_)), - pixelRatio(pixelRatio_), - observer(&nullObserver), - bin(size.width, size.height), - dirty(true) { -} - -SpriteAtlas::~SpriteAtlas() = default; - -void SpriteAtlas::load(const std::string& url, Scheduler& scheduler, FileSource& fileSource) { - if (url.empty()) { - // Treat a non-existent sprite as a successfully loaded empty sprite. - markAsLoaded(); - return; - } - - loader = std::make_unique<Loader>(scheduler, *this); - - loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) { - if (res.error) { - observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified) { - return; - } else if (res.noContent) { - loader->json = std::make_shared<const std::string>(); - emitSpriteLoadedIfComplete(); - } else { - // Only trigger a sprite loaded event we got new data. - loader->json = res.data; - emitSpriteLoadedIfComplete(); - } - }); - - loader->spriteRequest = fileSource.request(Resource::spriteImage(url, pixelRatio), [this](Response res) { - if (res.error) { - observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified) { - return; - } else if (res.noContent) { - loader->image = std::make_shared<const std::string>(); - emitSpriteLoadedIfComplete(); - } else { - loader->image = res.data; - emitSpriteLoadedIfComplete(); - } - }); -} - -void SpriteAtlas::emitSpriteLoadedIfComplete() { - assert(loader); - - if (!loader->image || !loader->json) { - return; - } - - loader->worker.invoke(&SpriteAtlasWorker::parse, loader->image, loader->json); - // TODO: delete the loader? -} - -void SpriteAtlas::onParsed(Images&& result) { - markAsLoaded(); - for (auto& pair : result) { - addImage(pair.first, std::move(pair.second)); - } - observer->onSpriteLoaded(); - for (auto requestor : requestors) { - requestor->onIconsAvailable(buildIconMap()); - } - requestors.clear(); -} - -void SpriteAtlas::onError(std::exception_ptr err) { - observer->onSpriteError(err); -} - -void SpriteAtlas::setObserver(SpriteAtlasObserver* observer_) { - observer = observer_; -} - -void SpriteAtlas::dumpDebugLogs() const { - Log::Info(Event::General, "SpriteAtlas::loaded: %d", loaded); -} - -void SpriteAtlas::addImage(const std::string& id, std::unique_ptr<style::Image> image_) { - icons.clear(); - - auto it = entries.find(id); - if (it == entries.end()) { - entries.emplace(id, Entry { std::move(image_), {}, {} }); - return; - } - - Entry& entry = it->second; - - // There is already a sprite with that name in our store. - if (entry.image->image.size != image_->image.size) { - Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", id.c_str()); - return; - } - - entry.image = std::move(image_); - - if (entry.iconRect) { - copy(entry, &Entry::iconRect); - } - - if (entry.patternRect) { - copy(entry, &Entry::patternRect); - } -} - -void SpriteAtlas::removeImage(const std::string& id) { - icons.clear(); - - auto it = entries.find(id); - if (it == entries.end()) { - return; - } - - Entry& entry = it->second; - - if (entry.iconRect) { - bin.release(*entry.iconRect); - } - - if (entry.patternRect) { - bin.release(*entry.patternRect); - } - - entries.erase(it); -} - -const style::Image* SpriteAtlas::getImage(const std::string& id) const { - const auto it = entries.find(id); - if (it != entries.end()) { - return it->second.image.get(); - } - if (!entries.empty()) { - Log::Info(Event::Sprite, "Can't find sprite named '%s'", id.c_str()); - } - return nullptr; -} - -void SpriteAtlas::getIcons(IconRequestor& requestor) { - if (isLoaded()) { - requestor.onIconsAvailable(buildIconMap()); - } else { - requestors.insert(&requestor); - } -} - -void SpriteAtlas::removeRequestor(IconRequestor& requestor) { - requestors.erase(&requestor); -} - -optional<SpriteAtlasElement> SpriteAtlas::getIcon(const std::string& id) { - return getImage(id, &Entry::iconRect); -} - -optional<SpriteAtlasElement> SpriteAtlas::getPattern(const std::string& id) { - return getImage(id, &Entry::patternRect); -} - -optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& id, - optional<Rect<uint16_t>> Entry::*entryRect) { - - auto it = entries.find(id); - if (it == entries.end()) { - if (!entries.empty()) { - Log::Info(Event::Sprite, "Can't find sprite named '%s'", id.c_str()); - } - return {}; - } - - Entry& entry = it->second; - - if (entry.*entryRect) { - assert(entry.image.get()); - return SpriteAtlasElement { - *(entry.*entryRect), - *entry.image, - size, - pixelRatio - }; - } - - const uint16_t pixelWidth = std::ceil(entry.image->image.size.width / pixelRatio); - const uint16_t pixelHeight = std::ceil(entry.image->image.size.height / pixelRatio); - - // Increase to next number divisible by 4, but at least 1. - // This is so we can scale down the texture coordinates and pack them - // into 2 bytes rather than 4 bytes. - const uint16_t packWidth = (pixelWidth + 1) + (4 - (pixelWidth + 1) % 4); - const uint16_t packHeight = (pixelHeight + 1) + (4 - (pixelHeight + 1) % 4); - - // We have to allocate a new area in the bin, and store an empty image in it. - Rect<uint16_t> rect = bin.allocate(packWidth, packHeight); - if (rect.w == 0) { - if (debug::spriteWarnings) { - Log::Warning(Event::Sprite, "sprite atlas bitmap overflow"); - } - return {}; - } - - entry.*entryRect = rect; - copy(entry, entryRect); - - return SpriteAtlasElement { - rect, - *entry.image, - size, - pixelRatio - }; -} - -void SpriteAtlas::copy(const Entry& entry, optional<Rect<uint16_t>> Entry::*entryRect) { - if (!image.valid()) { - image = PremultipliedImage({ static_cast<uint32_t>(std::ceil(size.width * pixelRatio)), - static_cast<uint32_t>(std::ceil(size.height * pixelRatio)) }); - image.fill(0); - } - - const PremultipliedImage& src = entry.image->image; - const Rect<uint16_t>& rect = *(entry.*entryRect); - - const uint32_t padding = 1; - const uint32_t x = (rect.x + padding) * pixelRatio; - const uint32_t y = (rect.y + padding) * pixelRatio; - const uint32_t w = src.size.width; - const uint32_t h = src.size.height; - - PremultipliedImage::copy(src, image, { 0, 0 }, { x, y }, { w, h }); - - if (entryRect == &Entry::patternRect) { - // Add 1 pixel wrapped padding on each side of the image. - PremultipliedImage::copy(src, image, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T - PremultipliedImage::copy(src, image, { 0, 0 }, { x, y + h }, { w, 1 }); // B - PremultipliedImage::copy(src, image, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L - PremultipliedImage::copy(src, image, { 0, 0 }, { x + w, y }, { 1, h }); // R - } - - dirty = true; -} - -IconMap SpriteAtlas::buildIconMap() { - if (icons.empty()) { - for (const auto& entry : entries) { - icons.emplace(std::piecewise_construct, - std::forward_as_tuple(entry.first), - std::forward_as_tuple(*getIcon(entry.first))); - - } - } - return icons; -} - -void SpriteAtlas::upload(gl::Context& context, gl::TextureUnit unit) { - if (!texture) { - texture = context.createTexture(image, unit); - } else if (dirty) { - context.updateTexture(*texture, image, unit); - } - -#if not MBGL_USE_GLES2 -// if (dirty) { -// platform::showColorDebugImage("Sprite Atlas", -// reinterpret_cast<const char*>(image.data.get()), size.width, -// size.height, image.size.width, image.size.height); -// } -#endif // MBGL_USE_GLES2 - - dirty = false; -} - -void SpriteAtlas::bind(bool linear, gl::Context& context, gl::TextureUnit unit) { - upload(context, unit); - context.bindTexture(*texture, unit, - linear ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest); -} - -} // namespace mbgl diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp deleted file mode 100644 index c3efeff44b..0000000000 --- a/src/mbgl/sprite/sprite_atlas.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include <mbgl/geometry/binpack.hpp> -#include <mbgl/gl/texture.hpp> -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/optional.hpp> -#include <mbgl/style/image.hpp> - -#include <string> -#include <map> -#include <set> -#include <unordered_map> -#include <array> -#include <memory> - -namespace mbgl { - -class Scheduler; -class FileSource; -class SpriteAtlasObserver; - -namespace gl { -class Context; -} // namespace gl - -class SpriteAtlasElement { -public: - SpriteAtlasElement(Rect<uint16_t>, const style::Image&, Size size, float pixelRatio); - - Rect<uint16_t> pos; - bool sdf; - - float relativePixelRatio; - std::array<float, 2> size; - std::array<float, 2> tl; - std::array<float, 2> br; - float width; - float height; -}; - -typedef std::map<std::string, SpriteAtlasElement> IconMap; -typedef std::set<std::string> IconDependencies; - -class IconRequestor { -public: - virtual ~IconRequestor() = default; - virtual void onIconsAvailable(IconMap) = 0; -}; - -class SpriteAtlas : public util::noncopyable { -public: - using Images = std::map<std::string, std::unique_ptr<style::Image>>; - - SpriteAtlas(Size, float pixelRatio); - ~SpriteAtlas(); - - void load(const std::string& url, Scheduler&, FileSource&); - - void markAsLoaded() { - loaded = true; - } - - bool isLoaded() const { - return loaded; - } - - void dumpDebugLogs() const; - - void setObserver(SpriteAtlasObserver*); - - const style::Image* getImage(const std::string&) const; - void addImage(const std::string&, std::unique_ptr<style::Image>); - void removeImage(const std::string&); - - void getIcons(IconRequestor& requestor); - void removeRequestor(IconRequestor& requestor); - - optional<SpriteAtlasElement> getIcon(const std::string& name); - optional<SpriteAtlasElement> getPattern(const std::string& name); - - // Binds the atlas texture to the GPU, and uploads data if it is out of date. - void bind(bool linear, gl::Context&, gl::TextureUnit unit); - - // Uploads the texture to the GPU to be available when we need it. This is a lazy operation; - // the texture is only bound when the data is out of date (=dirty). - void upload(gl::Context&, gl::TextureUnit unit); - - Size getSize() const { return size; } - float getPixelRatio() const { return pixelRatio; } - - // Only for use in tests. - const PremultipliedImage& getAtlasImage() const { - return image; - } - -private: - void emitSpriteLoadedIfComplete(); - - // Invoked by SpriteAtlasWorker - friend class SpriteAtlasWorker; - void onParsed(Images&& result); - void onError(std::exception_ptr); - - const Size size; - const float pixelRatio; - - struct Loader; - std::unique_ptr<Loader> loader; - - bool loaded = false; - - SpriteAtlasObserver* observer = nullptr; - - struct Entry { - std::unique_ptr<style::Image> image; - - // One sprite image might be used as both an icon image and a pattern image. If so, - // it must have two distinct entries in the texture. The one for the icon image has - // a single pixel transparent border, and the one for the pattern image has a single - // pixel border wrapped from the opposite side. - optional<Rect<uint16_t>> iconRect; - optional<Rect<uint16_t>> patternRect; - }; - - optional<SpriteAtlasElement> getImage(const std::string& name, optional<Rect<uint16_t>> Entry::*rect); - void copy(const Entry&, optional<Rect<uint16_t>> Entry::*rect); - - IconMap buildIconMap(); - - std::unordered_map<std::string, Entry> entries; - BinPack<uint16_t> bin; - PremultipliedImage image; - mbgl::optional<gl::Texture> texture; - bool dirty; - - std::set<IconRequestor*> requestors; - IconMap icons; -}; - -} // namespace mbgl diff --git a/src/mbgl/sprite/sprite_atlas_observer.hpp b/src/mbgl/sprite/sprite_atlas_observer.hpp deleted file mode 100644 index 263c95b0e8..0000000000 --- a/src/mbgl/sprite/sprite_atlas_observer.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include <exception> - -namespace mbgl { - -class SpriteAtlasObserver { -public: - virtual ~SpriteAtlasObserver() = default; - - virtual void onSpriteLoaded() {} - virtual void onSpriteError(std::exception_ptr) {} -}; - -} // namespace mbgl diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp new file mode 100644 index 0000000000..93d6dfd9ae --- /dev/null +++ b/src/mbgl/sprite/sprite_loader.cpp @@ -0,0 +1,104 @@ +#include <mbgl/sprite/sprite_loader.hpp> +#include <mbgl/sprite/sprite_loader_worker.hpp> +#include <mbgl/sprite/sprite_loader_observer.hpp> +#include <mbgl/sprite/sprite_parser.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/platform.hpp> +#include <mbgl/util/std.hpp> +#include <mbgl/util/constants.hpp> +#include <mbgl/util/exception.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> +#include <mbgl/actor/actor.hpp> +#include <mbgl/actor/scheduler.hpp> + +#include <cassert> + +namespace mbgl { + +static SpriteLoaderObserver nullObserver; + +struct SpriteLoader::Loader { + Loader(Scheduler& scheduler, SpriteLoader& imageManager) + : mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())), + worker(scheduler, ActorRef<SpriteLoader>(imageManager, mailbox)) { + } + + std::shared_ptr<const std::string> image; + std::shared_ptr<const std::string> json; + std::unique_ptr<AsyncRequest> jsonRequest; + std::unique_ptr<AsyncRequest> spriteRequest; + std::shared_ptr<Mailbox> mailbox; + Actor<SpriteLoaderWorker> worker; +}; + +SpriteLoader::SpriteLoader(float pixelRatio_) + : pixelRatio(pixelRatio_) + , observer(&nullObserver) { +} + +SpriteLoader::~SpriteLoader() = default; + +void SpriteLoader::load(const std::string& url, Scheduler& scheduler, FileSource& fileSource) { + if (url.empty()) { + // Treat a non-existent sprite as a successfully loaded empty sprite. + observer->onSpriteLoaded({}); + return; + } + + loader = std::make_unique<Loader>(scheduler, *this); + + loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) { + if (res.error) { + observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified) { + return; + } else if (res.noContent) { + loader->json = std::make_shared<const std::string>(); + emitSpriteLoadedIfComplete(); + } else { + // Only trigger a sprite loaded event we got new data. + loader->json = res.data; + emitSpriteLoadedIfComplete(); + } + }); + + loader->spriteRequest = fileSource.request(Resource::spriteImage(url, pixelRatio), [this](Response res) { + if (res.error) { + observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified) { + return; + } else if (res.noContent) { + loader->image = std::make_shared<const std::string>(); + emitSpriteLoadedIfComplete(); + } else { + loader->image = res.data; + emitSpriteLoadedIfComplete(); + } + }); +} + +void SpriteLoader::emitSpriteLoadedIfComplete() { + assert(loader); + + if (!loader->image || !loader->json) { + return; + } + + loader->worker.invoke(&SpriteLoaderWorker::parse, loader->image, loader->json); +} + +void SpriteLoader::onParsed(std::vector<std::unique_ptr<style::Image>>&& result) { + observer->onSpriteLoaded(std::move(result)); +} + +void SpriteLoader::onError(std::exception_ptr err) { + observer->onSpriteError(err); +} + +void SpriteLoader::setObserver(SpriteLoaderObserver* observer_) { + observer = observer_; +} + +} // namespace mbgl diff --git a/src/mbgl/sprite/sprite_loader.hpp b/src/mbgl/sprite/sprite_loader.hpp new file mode 100644 index 0000000000..0daf46be9c --- /dev/null +++ b/src/mbgl/sprite/sprite_loader.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/style/image.hpp> + +#include <string> +#include <map> +#include <set> +#include <vector> +#include <array> +#include <memory> + +namespace mbgl { + +class Scheduler; +class FileSource; +class SpriteLoaderObserver; + +class SpriteLoader : public util::noncopyable { +public: + SpriteLoader(float pixelRatio); + ~SpriteLoader(); + + void load(const std::string& url, Scheduler&, FileSource&); + + void setObserver(SpriteLoaderObserver*); + +private: + void emitSpriteLoadedIfComplete(); + + // Invoked by SpriteAtlasWorker + friend class SpriteLoaderWorker; + void onParsed(std::vector<std::unique_ptr<style::Image>>&&); + void onError(std::exception_ptr); + + const float pixelRatio; + + struct Loader; + std::unique_ptr<Loader> loader; + + SpriteLoaderObserver* observer = nullptr; +}; + +} // namespace mbgl diff --git a/src/mbgl/sprite/sprite_loader_observer.hpp b/src/mbgl/sprite/sprite_loader_observer.hpp new file mode 100644 index 0000000000..c730549c2c --- /dev/null +++ b/src/mbgl/sprite/sprite_loader_observer.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <exception> +#include <memory> +#include <vector> + +namespace mbgl { + +namespace style { +class Image; +} // namespace style + +class SpriteLoaderObserver { +public: + virtual ~SpriteLoaderObserver() = default; + + virtual void onSpriteLoaded(std::vector<std::unique_ptr<style::Image>>&&) {} + virtual void onSpriteError(std::exception_ptr) {} +}; + +} // namespace mbgl diff --git a/src/mbgl/sprite/sprite_atlas_worker.cpp b/src/mbgl/sprite/sprite_loader_worker.cpp index da507aabab..4bded33d53 100644 --- a/src/mbgl/sprite/sprite_atlas_worker.cpp +++ b/src/mbgl/sprite/sprite_loader_worker.cpp @@ -1,14 +1,14 @@ -#include <mbgl/sprite/sprite_atlas_worker.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> +#include <mbgl/sprite/sprite_loader_worker.hpp> +#include <mbgl/sprite/sprite_loader.hpp> #include <mbgl/sprite/sprite_parser.hpp> namespace mbgl { -SpriteAtlasWorker::SpriteAtlasWorker(ActorRef<SpriteAtlasWorker>, ActorRef<SpriteAtlas> parent_) +SpriteLoaderWorker::SpriteLoaderWorker(ActorRef<SpriteLoaderWorker>, ActorRef<SpriteLoader> parent_) : parent(std::move(parent_)) { } -void SpriteAtlasWorker::parse(std::shared_ptr<const std::string> image, +void SpriteLoaderWorker::parse(std::shared_ptr<const std::string> image, std::shared_ptr<const std::string> json) { try { if (!image) { @@ -20,9 +20,9 @@ void SpriteAtlasWorker::parse(std::shared_ptr<const std::string> image, throw std::runtime_error("missing sprite metadata"); } - parent.invoke(&SpriteAtlas::onParsed, parseSprite(*image, *json)); + parent.invoke(&SpriteLoader::onParsed, parseSprite(*image, *json)); } catch (...) { - parent.invoke(&SpriteAtlas::onError, std::current_exception()); + parent.invoke(&SpriteLoader::onError, std::current_exception()); } } diff --git a/src/mbgl/sprite/sprite_atlas_worker.hpp b/src/mbgl/sprite/sprite_loader_worker.hpp index 70ca0d52e5..d61e07d14f 100644 --- a/src/mbgl/sprite/sprite_atlas_worker.hpp +++ b/src/mbgl/sprite/sprite_loader_worker.hpp @@ -8,16 +8,16 @@ namespace mbgl { -class SpriteAtlas; +class SpriteLoader; -class SpriteAtlasWorker { +class SpriteLoaderWorker { public: - SpriteAtlasWorker(ActorRef<SpriteAtlasWorker>, ActorRef<SpriteAtlas>); + SpriteLoaderWorker(ActorRef<SpriteLoaderWorker>, ActorRef<SpriteLoader>); void parse(std::shared_ptr<const std::string> image, std::shared_ptr<const std::string> json); private: - ActorRef<SpriteAtlas> parent; + ActorRef<SpriteLoader> parent; }; } // namespace mbgl diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp index c3ed20d03f..1a36e3e990 100644 --- a/src/mbgl/sprite/sprite_parser.cpp +++ b/src/mbgl/sprite/sprite_parser.cpp @@ -13,13 +13,14 @@ namespace mbgl { -std::unique_ptr<style::Image> createStyleImage(const PremultipliedImage& image, - const uint32_t srcX, - const uint32_t srcY, - const uint32_t width, - const uint32_t height, - const double ratio, - const bool sdf) { +std::unique_ptr<style::Image> createStyleImage(const std::string& id, + const PremultipliedImage& image, + const uint32_t srcX, + const uint32_t srcY, + const uint32_t width, + const uint32_t height, + const double ratio, + const bool sdf) { // Disallow invalid parameter configurations. if (width <= 0 || height <= 0 || width > 1024 || height > 1024 || ratio <= 0 || ratio > 10 || @@ -37,7 +38,7 @@ std::unique_ptr<style::Image> createStyleImage(const PremultipliedImage& image, // Copy from the source image into our individual sprite image PremultipliedImage::copy(image, dstImage, { srcX, srcY }, { 0, 0 }, { width, height }); - return std::make_unique<style::Image>(std::move(dstImage), ratio, sdf); + return std::make_unique<style::Image>(id, std::move(dstImage), ratio, sdf); } namespace { @@ -84,7 +85,7 @@ bool getBoolean(const JSValue& value, const char* name, const bool def = false) } // namespace -Images parseSprite(const std::string& encodedImage, const std::string& json) { +std::vector<std::unique_ptr<style::Image>> parseSprite(const std::string& encodedImage, const std::string& json) { const PremultipliedImage raster = decodeImage(encodedImage); JSDocument doc; @@ -96,7 +97,7 @@ Images parseSprite(const std::string& encodedImage, const std::string& json) { } else if (!doc.IsObject()) { throw std::runtime_error("Sprite JSON root must be an object"); } else { - Images images; + std::vector<std::unique_ptr<style::Image>> images; for (const auto& property : doc.GetObject()) { const std::string name = { property.name.GetString(), property.name.GetStringLength() }; const JSValue& value = property.value; @@ -109,9 +110,9 @@ Images parseSprite(const std::string& encodedImage, const std::string& json) { const double pixelRatio = getDouble(value, "pixelRatio", 1); const bool sdf = getBoolean(value, "sdf", false); - auto image = createStyleImage(raster, x, y, width, height, pixelRatio, sdf); + auto image = createStyleImage(name, raster, x, y, width, height, pixelRatio, sdf); if (image) { - images.emplace(name, std::move(image)); + images.push_back(std::move(image)); } } } diff --git a/src/mbgl/sprite/sprite_parser.hpp b/src/mbgl/sprite/sprite_parser.hpp index 5be8435ebb..f602818d3b 100644 --- a/src/mbgl/sprite/sprite_parser.hpp +++ b/src/mbgl/sprite/sprite_parser.hpp @@ -1,13 +1,10 @@ #pragma once #include <mbgl/util/image.hpp> -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/variant.hpp> -#include <mbgl/util/geo.hpp> #include <string> #include <memory> -#include <map> +#include <vector> namespace mbgl { @@ -16,17 +13,16 @@ class Image; } // namespace style // Extracts an individual image from a spritesheet from the given location. -std::unique_ptr<style::Image> createStyleImage(const PremultipliedImage&, - uint32_t srcX, - uint32_t srcY, - uint32_t srcWidth, - uint32_t srcHeight, - double ratio, - bool sdf); - -using Images = std::map<std::string, std::unique_ptr<style::Image>>; +std::unique_ptr<style::Image> createStyleImage(const std::string& id, + const PremultipliedImage&, + uint32_t srcX, + uint32_t srcY, + uint32_t srcWidth, + uint32_t srcHeight, + double ratio, + bool sdf); // Parses an image and an associated JSON file and returns the sprite objects. -Images parseSprite(const std::string& image, const std::string& json); +std::vector<std::unique_ptr<style::Image>> parseSprite(const std::string& image, const std::string& json); } // namespace mbgl diff --git a/src/mbgl/storage/asset_file_source.hpp b/src/mbgl/storage/asset_file_source.hpp index 71e5bbdab3..6ed7af8aaf 100644 --- a/src/mbgl/storage/asset_file_source.hpp +++ b/src/mbgl/storage/asset_file_source.hpp @@ -17,7 +17,8 @@ public: private: class Impl; - std::unique_ptr<util::Thread<Impl>> thread; + + std::unique_ptr<util::Thread<Impl>> impl; }; } // namespace mbgl diff --git a/src/mbgl/storage/file_source_request.cpp b/src/mbgl/storage/file_source_request.cpp new file mode 100644 index 0000000000..09ea8cc32a --- /dev/null +++ b/src/mbgl/storage/file_source_request.cpp @@ -0,0 +1,37 @@ +#include <mbgl/storage/file_source_request.hpp> + +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/scheduler.hpp> + +namespace mbgl { + +FileSourceRequest::FileSourceRequest(FileSource::Callback&& callback) + : responseCallback(callback) + , mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())) { +} + +FileSourceRequest::~FileSourceRequest() { + if (cancelCallback) { + cancelCallback(); + } + + mailbox->close(); +} + +void FileSourceRequest::onCancel(std::function<void()>&& callback) { + cancelCallback = std::move(callback); +} + +void FileSourceRequest::setResponse(const Response& response) { + // Copy, because calling the callback will sometimes self + // destroy this object. We cannot move because this method + // can be called more than one. + auto callback = responseCallback; + callback(response); +} + +ActorRef<FileSourceRequest> FileSourceRequest::actor() { + return ActorRef<FileSourceRequest>(*this, mailbox); +} + +} // namespace mbgl diff --git a/src/mbgl/storage/file_source_request.hpp b/src/mbgl/storage/file_source_request.hpp new file mode 100644 index 0000000000..6bd0d44df6 --- /dev/null +++ b/src/mbgl/storage/file_source_request.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <mbgl/actor/actor_ref.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/util/async_request.hpp> + +#include <memory> +#include <functional> + +namespace mbgl { + +class Mailbox; + +class FileSourceRequest : public AsyncRequest { +public: + FileSourceRequest(FileSource::Callback&& callback); + ~FileSourceRequest() final; + + void onCancel(std::function<void()>&& callback); + void setResponse(const Response& res); + + ActorRef<FileSourceRequest> actor(); + +private: + FileSource::Callback responseCallback = nullptr; + std::function<void()> cancelCallback = nullptr; + + std::shared_ptr<Mailbox> mailbox; +}; + +} // namespace mbgl diff --git a/src/mbgl/storage/local_file_source.hpp b/src/mbgl/storage/local_file_source.hpp index 43319bc06e..0f065e0b5f 100644 --- a/src/mbgl/storage/local_file_source.hpp +++ b/src/mbgl/storage/local_file_source.hpp @@ -19,7 +19,8 @@ public: private: class Impl; - std::unique_ptr<util::Thread<Impl>> thread; + + std::unique_ptr<util::Thread<Impl>> impl; }; } // namespace mbgl diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp index 20dde1db56..94bba7f8bf 100644 --- a/src/mbgl/storage/resource.cpp +++ b/src/mbgl/storage/resource.cpp @@ -53,6 +53,13 @@ Resource Resource::source(const std::string& url) { }; } +Resource Resource::image(const std::string& url) { + return Resource { + Resource::Kind::Image, + url + }; +} + Resource Resource::spriteImage(const std::string& base, float pixelRatio) { return Resource { Resource::Kind::SpriteImage, diff --git a/src/mbgl/storage/resource_transform.cpp b/src/mbgl/storage/resource_transform.cpp new file mode 100644 index 0000000000..a5e62b2c1a --- /dev/null +++ b/src/mbgl/storage/resource_transform.cpp @@ -0,0 +1,13 @@ +#include <mbgl/storage/resource_transform.hpp> + +namespace mbgl { + +ResourceTransform::ResourceTransform(ActorRef<ResourceTransform>, TransformCallback&& callback) + : transformCallback(std::move(callback)) { +} + +void ResourceTransform::transform(Resource::Kind kind, const std::string&& url, FinishedCallback&& finished) { + finished(transformCallback(kind, std::move(url))); +} + +} // namespace mbgl diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp index a174339074..222f55db84 100644 --- a/src/mbgl/storage/response.cpp +++ b/src/mbgl/storage/response.cpp @@ -14,6 +14,7 @@ Response& Response::operator=(const Response& res) { error = res.error ? std::make_unique<Error>(*res.error) : nullptr; noContent = res.noContent; notModified = res.notModified; + mustRevalidate = res.mustRevalidate; data = res.data; modified = res.modified; expires = res.expires; diff --git a/src/mbgl/style/class_dictionary.cpp b/src/mbgl/style/class_dictionary.cpp deleted file mode 100644 index ec06ee7d9d..0000000000 --- a/src/mbgl/style/class_dictionary.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include <mbgl/style/class_dictionary.hpp> - -#include <pthread.h> - -namespace mbgl { -namespace style { - -ClassDictionary::ClassDictionary() {} - -ClassDictionary &ClassDictionary::Get() { - static pthread_once_t store_once = PTHREAD_ONCE_INIT; - static pthread_key_t store_key; - - // Create the key. - pthread_once(&store_once, []() { - pthread_key_create(&store_key, [](void *ptr) { - delete reinterpret_cast<ClassDictionary *>(ptr); - }); - }); - - ClassDictionary *ptr = reinterpret_cast<ClassDictionary *>(pthread_getspecific(store_key)); - if (ptr == nullptr) { - ptr = new ClassDictionary(); - pthread_setspecific(store_key, ptr); - } - - return *ptr; -} - -ClassID ClassDictionary::lookup(const std::string &class_name) { - auto it = store.find(class_name); - if (it == store.end()) { - // Insert the class name into the store. - ClassID id = ClassID(uint32_t(ClassID::Named) + offset++); - store.emplace(class_name, id); - return id; - } else { - return it->second; - } -} - -ClassID ClassDictionary::normalize(ClassID id) { - if (id >= ClassID::Named) { - return ClassID::Named; - } else { - return id; - } -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/class_dictionary.hpp b/src/mbgl/style/class_dictionary.hpp deleted file mode 100644 index 37eb488240..0000000000 --- a/src/mbgl/style/class_dictionary.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include <cstdint> -#include <string> -#include <unordered_map> -#include <functional> - -namespace mbgl { -namespace style { - -enum class ClassID : uint32_t { - Default = 1, // These values are from the default style for a layer - Named = 2 // These values (and all subsequent IDs) are from a named style from the layer -}; - -class ClassDictionary { -private: - ClassDictionary(); - -public: - static ClassDictionary &Get(); - - // Returns an ID for a class name. If the class name does not yet have an ID, one is - // auto-generated and stored for future reference. - ClassID lookup(const std::string &class_name); - - // Returns either Fallback, Default or Named, depending on the type of the class id. - ClassID normalize(ClassID id); - -private: - std::unordered_map<std::string, ClassID> store = { { "", ClassID::Default } }; - uint32_t offset = 0; -}; - -} // namespace style -} // namespace mbgl - -namespace std { - -// Explicitly define std::hash<style::ClassID> because GCC doesn't automatically use std::hash<> of -// the underlying enum type. - -template <> -struct hash<mbgl::style::ClassID> { -public: - size_t operator()(const mbgl::style::ClassID id) const { - using T = std::underlying_type_t<mbgl::style::ClassID>; - return std::hash<T>()(static_cast<T>(id)); - } -}; - -} // namespace std diff --git a/src/mbgl/style/collection.hpp b/src/mbgl/style/collection.hpp new file mode 100644 index 0000000000..0deb1411b6 --- /dev/null +++ b/src/mbgl/style/collection.hpp @@ -0,0 +1,141 @@ +#pragma once + +#include <mbgl/util/immutable.hpp> +#include <mbgl/util/optional.hpp> + +#include <memory> +#include <string> + +namespace mbgl { +namespace style { + +/* + Manages an ordered collection of elements and their `Immutable<Impl>`s. The latter is + itself stored in an Immutable container. Using immutability at the collection level + allows us to short-circuit significant portions of the RenderStyle update logic via + a simple pointer equality check, greatly improving performance. + + Element types are required to have: + + * An `Impl` inner class type + * An `Immutable<Impl> baseImpl` member + * A `std::string getID() const` method +*/ +template <class T> +class Collection { +public: + using Impl = typename T::Impl; + using WrapperVector = std::vector<std::unique_ptr<T>>; + using ImmutableVector = Immutable<std::vector<Immutable<Impl>>>; + + Collection(); + + std::size_t size() const; + T* get(const std::string&) const; + + std::vector<T*> getWrappers() const; + ImmutableVector getImpls() const { return impls; } + + auto begin() const { return wrappers.begin(); } + auto end() const { return wrappers.end(); } + + void clear(); + + T* add(std::unique_ptr<T>, const optional<std::string>& = {}); + std::unique_ptr<T> remove(const std::string&); + + // Must be called whenever an element of the collection is internally mutated. + // Typically, each element permits registration of an observer, and the observer + // should call this method. + void update(const T&); + +private: + std::size_t index(const std::string&) const; + + WrapperVector wrappers; + ImmutableVector impls; +}; + +template <class T> +Collection<T>::Collection() + : impls(makeMutable<std::vector<Immutable<Impl>>>()) { +} + +template <class T> +std::size_t Collection<T>::size() const { + return wrappers.size(); +} + +template <class T> +std::size_t Collection<T>::index(const std::string& id) const { + return std::find_if(wrappers.begin(), wrappers.end(), [&](const auto& e) { + return e->getID() == id; + }) - wrappers.begin(); +} + +template <class T> +T* Collection<T>::get(const std::string& id) const { + std::size_t i = index(id); + return i < size() ? wrappers[i].get() : nullptr; +} + +template <class T> +std::vector<T*> Collection<T>::getWrappers() const { + std::vector<T*> result; + result.reserve(wrappers.size()); + + for (auto& wrapper : wrappers) { + result.push_back(wrapper.get()); + } + + return result; +} + +template <class T> +void Collection<T>::clear() { + mutate(impls, [&] (auto& impls_) { + impls_.clear(); + }); + + wrappers.clear(); +} + +template <class T> +T* Collection<T>::add(std::unique_ptr<T> wrapper, const optional<std::string>& before) { + std::size_t i = before ? index(*before) : size(); + + mutate(impls, [&] (auto& impls_) { + impls_.emplace(impls_.begin() + i, wrapper->baseImpl); + }); + + return wrappers.emplace(wrappers.begin() + i, std::move(wrapper))->get(); +} + +template <class T> +std::unique_ptr<T> Collection<T>::remove(const std::string& id) { + std::size_t i = index(id); + + if (i >= size()) { + return nullptr; + } + + auto source = std::move(wrappers[i]); + + mutate(impls, [&] (auto& impls_) { + impls_.erase(impls_.begin() + i); + }); + + wrappers.erase(wrappers.begin() + i); + + return source; +} + +template <class T> +void Collection<T>::update(const T& wrapper) { + mutate(impls, [&] (auto& impls_) { + impls_.at(this->index(wrapper.getID())) = wrapper.baseImpl; + }); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 4afbf198e5..6ae6fede42 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -2,7 +2,7 @@ #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> -#include <mbgl/style/layout_property.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/enum.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/feature.hpp> @@ -446,13 +446,6 @@ void stringify(Writer& writer, const DataDrivenPropertyValue<T>& value) { } } -template <class Writer, class... Ps> -void stringify(Writer& writer, const LayoutProperties<Ps...>& ps) { - writer.StartObject(); - util::ignore({ (stringify<Ps>(writer, ps.unevaluated.template get<Ps>()), 0)... }); - writer.EndObject(); -} - } // namespace conversion } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/function/categorical_stops.cpp b/src/mbgl/style/function/categorical_stops.cpp index 2984c3832f..dd179f5376 100644 --- a/src/mbgl/style/function/categorical_stops.cpp +++ b/src/mbgl/style/function/categorical_stops.cpp @@ -33,6 +33,9 @@ template class CategoricalStops<Color>; template class CategoricalStops<std::array<float, 2>>; template class CategoricalStops<std::string>; template class CategoricalStops<TextTransformType>; +template class CategoricalStops<TextJustifyType>; +template class CategoricalStops<SymbolAnchorType>; +template class CategoricalStops<LineJoinType>; } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/function/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp index dfb34e9dd4..0ac6fda846 100644 --- a/src/mbgl/style/function/identity_stops.cpp +++ b/src/mbgl/style/function/identity_stops.cpp @@ -36,25 +36,53 @@ optional<TextTransformType> IdentityStops<TextTransformType>::evaluate(const Val if (!value.is<std::string>()) { return {}; } - + return Enum<TextTransformType>::toEnum(value.get<std::string>()); } template <> +optional<TextJustifyType> IdentityStops<TextJustifyType>::evaluate(const Value& value) const { + if (!value.is<std::string>()) { + return {}; + } + + return Enum<TextJustifyType>::toEnum(value.get<std::string>()); +} + +template <> +optional<SymbolAnchorType> IdentityStops<SymbolAnchorType>::evaluate(const Value& value) const { + if (!value.is<std::string>()) { + return {}; + } + + return Enum<SymbolAnchorType>::toEnum(value.get<std::string>()); +} + +template <> +optional<LineJoinType> IdentityStops<LineJoinType>::evaluate(const Value& value) const { + if (!value.is<std::string>()) { + return {}; + } + + return Enum<LineJoinType>::toEnum(value.get<std::string>()); +} + +template <> optional<std::array<float, 2>> IdentityStops<std::array<float, 2>>::evaluate(const Value& value) const { if (!value.is<std::vector<Value>>()) { return {}; } - const std::vector<Value>& vector = value.get<std::vector<Value>>(); + const auto& vector = value.get<std::vector<Value>>(); if (vector.size() != 2 || !numericValue<float>(vector[0]) || !numericValue<float>(vector[1])) { return {}; } - return {{{ + std::array<float, 2> array {{ *numericValue<float>(vector[0]), *numericValue<float>(vector[1]) - }}}; + }}; + return array; } } // namespace style diff --git a/src/mbgl/style/image.cpp b/src/mbgl/style/image.cpp index 4c0c6a859b..1747de5fcc 100644 --- a/src/mbgl/style/image.cpp +++ b/src/mbgl/style/image.cpp @@ -1,21 +1,33 @@ #include <mbgl/style/image.hpp> +#include <mbgl/style/image_impl.hpp> #include <mbgl/util/exception.hpp> namespace mbgl { namespace style { -Image::Image(PremultipliedImage&& image_, - const float pixelRatio_, - bool sdf_) - : image(std::move(image_)), - pixelRatio(pixelRatio_), - sdf(sdf_) { - - if (!image.valid()) { - throw util::SpriteImageException("Sprite image dimensions may not be zero"); - } else if (pixelRatio <= 0) { - throw util::SpriteImageException("Sprite pixelRatio may not be <= 0"); - } +Image::Image(std::string id, + PremultipliedImage &&image, + const float pixelRatio, + bool sdf) + : baseImpl(makeMutable<Impl>(std::move(id), std::move(image), pixelRatio, sdf)) { +} + +std::string Image::getID() const { + return baseImpl->id; +} + +Image::Image(const Image&) = default; + +const PremultipliedImage& Image::getImage() const { + return baseImpl->image; +} + +bool Image::isSdf() const { + return baseImpl->sdf; +} + +float Image::getPixelRatio() const { + return baseImpl->pixelRatio; } } // namespace style diff --git a/src/mbgl/style/image_impl.cpp b/src/mbgl/style/image_impl.cpp new file mode 100644 index 0000000000..ce327262e8 --- /dev/null +++ b/src/mbgl/style/image_impl.cpp @@ -0,0 +1,24 @@ +#include <mbgl/style/image_impl.hpp> +#include <mbgl/util/exception.hpp> + +namespace mbgl { +namespace style { + +Image::Impl::Impl(std::string id_, + PremultipliedImage&& image_, + const float pixelRatio_, + bool sdf_) + : id(std::move(id_)), + image(std::move(image_)), + pixelRatio(pixelRatio_), + sdf(sdf_) { + + if (!image.valid()) { + throw util::SpriteImageException("Sprite image dimensions may not be zero"); + } else if (pixelRatio <= 0) { + throw util::SpriteImageException("Sprite pixelRatio may not be <= 0"); + } +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp new file mode 100644 index 0000000000..e439e42695 --- /dev/null +++ b/src/mbgl/style/image_impl.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include <mbgl/style/image.hpp> + +#include <string> +#include <unordered_map> +#include <set> + +namespace mbgl { +namespace style { + +class Image::Impl { +public: + Impl(std::string id, PremultipliedImage&&, float pixelRatio, bool sdf = false); + + const std::string id; + + PremultipliedImage image; + + // Pixel ratio of the sprite image. + const float pixelRatio; + + // Whether this image should be interpreted as a signed distance field icon. + const bool sdf; +}; + +} // namespace style + +using ImageMap = std::unordered_map<std::string, Immutable<style::Image::Impl>>; +using ImageDependencies = std::set<std::string>; +using ImageRequestPair = std::pair<ImageDependencies, uint64_t>; + +} // namespace mbgl diff --git a/src/mbgl/style/layer.cpp b/src/mbgl/style/layer.cpp index e2eba0e2e0..142fe313cf 100644 --- a/src/mbgl/style/layer.cpp +++ b/src/mbgl/style/layer.cpp @@ -1,17 +1,24 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/layer_impl.hpp> -#include <mbgl/style/layer_type.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { -Layer::Layer(LayerType type_, std::unique_ptr<Impl> baseImpl_) - : type(type_), baseImpl(std::move(baseImpl_)) { +static LayerObserver nullObserver; + +Layer::Layer(Immutable<Impl> impl) + : baseImpl(std::move(impl)), + observer(&nullObserver) { } Layer::~Layer() = default; -const std::string& Layer::getID() const { +LayerType Layer::getType() const { + return baseImpl->type; +} + +std::string Layer::getID() const { return baseImpl->id; } @@ -19,27 +26,16 @@ VisibilityType Layer::getVisibility() const { return baseImpl->visibility; } -void Layer::setVisibility(VisibilityType value) { - if (value == getVisibility()) - return; - baseImpl->visibility = value; - baseImpl->observer->onLayerVisibilityChanged(*this); -} - float Layer::getMinZoom() const { return baseImpl->minZoom; } -void Layer::setMinZoom(float minZoom) const { - baseImpl->minZoom = minZoom; -} - float Layer::getMaxZoom() const { return baseImpl->maxZoom; } -void Layer::setMaxZoom(float maxZoom) const { - baseImpl->maxZoom = maxZoom; +void Layer::setObserver(LayerObserver* observer_) { + observer = observer_ ? observer_ : &nullObserver; } } // namespace style diff --git a/src/mbgl/style/layer_impl.cpp b/src/mbgl/style/layer_impl.cpp index b8eb01fe77..a9a3941f3e 100644 --- a/src/mbgl/style/layer_impl.cpp +++ b/src/mbgl/style/layer_impl.cpp @@ -3,16 +3,10 @@ namespace mbgl { namespace style { -std::unique_ptr<Layer> Layer::Impl::copy(const std::string& id_, - const std::string& source_) const { - std::unique_ptr<Layer> result = clone(); - result->baseImpl->id = id_; - result->baseImpl->source = source_; - return result; -} - -void Layer::Impl::setObserver(LayerObserver* observer_) { - observer = observer_ ? observer_ : &nullObserver; +Layer::Impl::Impl(LayerType type_, std::string layerID, std::string sourceID) + : type(type_), + id(std::move(layerID)), + source(std::move(sourceID)) { } } // namespace style diff --git a/src/mbgl/style/layer_impl.hpp b/src/mbgl/style/layer_impl.hpp index 6aa8fccaf0..f350044925 100644 --- a/src/mbgl/style/layer_impl.hpp +++ b/src/mbgl/style/layer_impl.hpp @@ -3,13 +3,10 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/types.hpp> #include <mbgl/style/filter.hpp> -#include <mbgl/style/layer_observer.hpp> -#include <mbgl/util/noncopyable.hpp> #include <rapidjson/writer.h> #include <rapidjson/stringbuffer.h> -#include <memory> #include <string> #include <limits> @@ -32,27 +29,19 @@ namespace style { */ class Layer::Impl { public: + Impl(LayerType, std::string layerID, std::string sourceID); virtual ~Impl() = default; - // Create a new layer with the specified `id` and `sourceID`. All other properties - // are copied from this layer. - std::unique_ptr<Layer> copy(const std::string& id, - const std::string& sourceID) const; - - // Create an identical copy of this layer. - virtual std::unique_ptr<Layer> clone() const = 0; + Impl& operator=(const Impl&) = delete; - // Create a layer, copying all properties except id and paint properties from this layer. - virtual std::unique_ptr<Layer> cloneRef(const std::string& id) const = 0; + // Returns true buckets if properties affecting layout have changed: i.e. filter, + // visibility, layout properties, or data-driven paint properties. + virtual bool hasLayoutDifference(const Layer::Impl&) const = 0; // Utility function for automatic layer grouping. virtual void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const = 0; - virtual std::unique_ptr<RenderLayer> createRenderLayer() const = 0; - - void setObserver(LayerObserver*); - -public: + const LayerType type; std::string id; std::string source; std::string sourceLayer; @@ -61,13 +50,8 @@ public: float maxZoom = std::numeric_limits<float>::infinity(); VisibilityType visibility = VisibilityType::Visible; - LayerObserver nullObserver; - LayerObserver* observer = &nullObserver; - protected: - Impl() = default; Impl(const Impl&) = default; - Impl& operator=(const Impl&) = delete; }; } // namespace style diff --git a/src/mbgl/style/layer_observer.hpp b/src/mbgl/style/layer_observer.hpp index 2fa1c39660..28074a65e7 100644 --- a/src/mbgl/style/layer_observer.hpp +++ b/src/mbgl/style/layer_observer.hpp @@ -9,11 +9,7 @@ class LayerObserver { public: virtual ~LayerObserver() = default; - virtual void onLayerFilterChanged(Layer&) {} - virtual void onLayerVisibilityChanged(Layer&) {} - virtual void onLayerPaintPropertyChanged(Layer&) {} - virtual void onLayerDataDrivenPaintPropertyChanged(Layer&) {} - virtual void onLayerLayoutPropertyChanged(Layer&, const char *) {} + virtual void onLayerChanged(Layer&) {} }; } // namespace style diff --git a/src/mbgl/style/layers/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp index b4ffea138b..d4ead18816 100644 --- a/src/mbgl/style/layers/background_layer.cpp +++ b/src/mbgl/style/layers/background_layer.cpp @@ -2,39 +2,65 @@ #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { BackgroundLayer::BackgroundLayer(const std::string& layerID) - : Layer(LayerType::Background, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; + : Layer(makeMutable<Impl>(LayerType::Background, layerID, std::string())) { } -BackgroundLayer::BackgroundLayer(const Impl& other) - : Layer(LayerType::Background, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +BackgroundLayer::BackgroundLayer(Immutable<Impl> impl_) + : Layer(std::move(impl_)) { } BackgroundLayer::~BackgroundLayer() = default; -std::unique_ptr<Layer> BackgroundLayer::Impl::clone() const { - return std::make_unique<BackgroundLayer>(*this); +const BackgroundLayer::Impl& BackgroundLayer::impl() const { + return static_cast<const Impl&>(*baseImpl); } -std::unique_ptr<Layer> BackgroundLayer::Impl::cloneRef(const std::string& id_) const { - auto result = std::make_unique<BackgroundLayer>(*this); - result->impl->id = id_; - result->impl->cascading = BackgroundPaintProperties::Cascading(); - return std::move(result); +Mutable<BackgroundLayer::Impl> BackgroundLayer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> BackgroundLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = BackgroundPaintProperties::Transitionable(); + return std::make_unique<BackgroundLayer>(std::move(impl_)); } void BackgroundLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const { } +// Visibility + +void BackgroundLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void BackgroundLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void BackgroundLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); +} + // Layout properties @@ -44,69 +70,81 @@ PropertyValue<Color> BackgroundLayer::getDefaultBackgroundColor() { return { Color::black() }; } -PropertyValue<Color> BackgroundLayer::getBackgroundColor(const optional<std::string>& klass) const { - return impl->cascading.template get<BackgroundColor>().get(klass); +PropertyValue<Color> BackgroundLayer::getBackgroundColor() const { + return impl().paint.template get<BackgroundColor>().value; } -void BackgroundLayer::setBackgroundColor(PropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getBackgroundColor(klass)) +void BackgroundLayer::setBackgroundColor(PropertyValue<Color> value) { + if (value == getBackgroundColor()) return; - impl->cascading.template get<BackgroundColor>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<BackgroundColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void BackgroundLayer::setBackgroundColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<BackgroundColor>().setTransition(value, klass); +void BackgroundLayer::setBackgroundColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<BackgroundColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions BackgroundLayer::getBackgroundColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<BackgroundColor>().getTransition(klass); +TransitionOptions BackgroundLayer::getBackgroundColorTransition() const { + return impl().paint.template get<BackgroundColor>().options; } PropertyValue<std::string> BackgroundLayer::getDefaultBackgroundPattern() { return { "" }; } -PropertyValue<std::string> BackgroundLayer::getBackgroundPattern(const optional<std::string>& klass) const { - return impl->cascading.template get<BackgroundPattern>().get(klass); +PropertyValue<std::string> BackgroundLayer::getBackgroundPattern() const { + return impl().paint.template get<BackgroundPattern>().value; } -void BackgroundLayer::setBackgroundPattern(PropertyValue<std::string> value, const optional<std::string>& klass) { - if (value == getBackgroundPattern(klass)) +void BackgroundLayer::setBackgroundPattern(PropertyValue<std::string> value) { + if (value == getBackgroundPattern()) return; - impl->cascading.template get<BackgroundPattern>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<BackgroundPattern>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void BackgroundLayer::setBackgroundPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<BackgroundPattern>().setTransition(value, klass); +void BackgroundLayer::setBackgroundPatternTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<BackgroundPattern>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions BackgroundLayer::getBackgroundPatternTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<BackgroundPattern>().getTransition(klass); +TransitionOptions BackgroundLayer::getBackgroundPatternTransition() const { + return impl().paint.template get<BackgroundPattern>().options; } PropertyValue<float> BackgroundLayer::getDefaultBackgroundOpacity() { return { 1 }; } -PropertyValue<float> BackgroundLayer::getBackgroundOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<BackgroundOpacity>().get(klass); +PropertyValue<float> BackgroundLayer::getBackgroundOpacity() const { + return impl().paint.template get<BackgroundOpacity>().value; } -void BackgroundLayer::setBackgroundOpacity(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getBackgroundOpacity(klass)) +void BackgroundLayer::setBackgroundOpacity(PropertyValue<float> value) { + if (value == getBackgroundOpacity()) return; - impl->cascading.template get<BackgroundOpacity>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<BackgroundOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void BackgroundLayer::setBackgroundOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<BackgroundOpacity>().setTransition(value, klass); +void BackgroundLayer::setBackgroundOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<BackgroundOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions BackgroundLayer::getBackgroundOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<BackgroundOpacity>().getTransition(klass); +TransitionOptions BackgroundLayer::getBackgroundOpacityTransition() const { + return impl().paint.template get<BackgroundOpacity>().options; } } // namespace style diff --git a/src/mbgl/style/layers/background_layer_impl.cpp b/src/mbgl/style/layers/background_layer_impl.cpp index 6c4a4c26d9..a59a84fbe9 100644 --- a/src/mbgl/style/layers/background_layer_impl.cpp +++ b/src/mbgl/style/layers/background_layer_impl.cpp @@ -1,11 +1,10 @@ #include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/renderer/render_background_layer.hpp> namespace mbgl { namespace style { -std::unique_ptr<RenderLayer> BackgroundLayer::Impl::createRenderLayer() const { - return std::make_unique<RenderBackgroundLayer>(*this); +bool BackgroundLayer::Impl::hasLayoutDifference(const Layer::Impl&) const { + return false; } } // namespace style diff --git a/src/mbgl/style/layers/background_layer_impl.hpp b/src/mbgl/style/layers/background_layer_impl.hpp index 85152da4ec..248a751027 100644 --- a/src/mbgl/style/layers/background_layer_impl.hpp +++ b/src/mbgl/style/layers/background_layer_impl.hpp @@ -9,13 +9,12 @@ namespace style { class BackgroundLayer::Impl : public Layer::Impl { public: - std::unique_ptr<Layer> clone() const override; - std::unique_ptr<Layer> cloneRef(const std::string& id) const override; - void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; + using Layer::Impl::Impl; - std::unique_ptr<RenderLayer> createRenderLayer() const override; + bool hasLayoutDifference(const Layer::Impl&) const override; + void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - BackgroundPaintProperties::Cascading cascading; + BackgroundPaintProperties::Transitionable paint; }; } // namespace style diff --git a/src/mbgl/style/layers/background_layer_properties.hpp b/src/mbgl/style/layers/background_layer_properties.hpp index fae6c26a4b..3a61392fb4 100644 --- a/src/mbgl/style/layers/background_layer_properties.hpp +++ b/src/mbgl/style/layers/background_layer_properties.hpp @@ -5,7 +5,9 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/programs/attributes.hpp> +#include <mbgl/programs/uniforms.hpp> namespace mbgl { namespace style { @@ -22,7 +24,7 @@ struct BackgroundOpacity : PaintProperty<float> { static float defaultValue() { return 1; } }; -class BackgroundPaintProperties : public PaintProperties< +class BackgroundPaintProperties : public Properties< BackgroundColor, BackgroundPattern, BackgroundOpacity diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp index 8b3431a9a1..9854932699 100644 --- a/src/mbgl/style/layers/circle_layer.cpp +++ b/src/mbgl/style/layers/circle_layer.cpp @@ -2,34 +2,34 @@ #include <mbgl/style/layers/circle_layer.hpp> #include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { CircleLayer::CircleLayer(const std::string& layerID, const std::string& sourceID) - : Layer(LayerType::Circle, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; - impl->source = sourceID; + : Layer(makeMutable<Impl>(LayerType::Circle, layerID, sourceID)) { } -CircleLayer::CircleLayer(const Impl& other) - : Layer(LayerType::Circle, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +CircleLayer::CircleLayer(Immutable<Impl> impl_) + : Layer(std::move(impl_)) { } CircleLayer::~CircleLayer() = default; -std::unique_ptr<Layer> CircleLayer::Impl::clone() const { - return std::make_unique<CircleLayer>(*this); +const CircleLayer::Impl& CircleLayer::impl() const { + return static_cast<const Impl&>(*baseImpl); } -std::unique_ptr<Layer> CircleLayer::Impl::cloneRef(const std::string& id_) const { - auto result = std::make_unique<CircleLayer>(*this); - result->impl->id = id_; - result->impl->cascading = CirclePaintProperties::Cascading(); - return std::move(result); +Mutable<CircleLayer::Impl> CircleLayer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> CircleLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = CirclePaintProperties::Transitionable(); + return std::make_unique<CircleLayer>(std::move(impl_)); } void CircleLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const { @@ -38,26 +38,55 @@ void CircleLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffe // Source const std::string& CircleLayer::getSourceID() const { - return impl->source; + return impl().source; } void CircleLayer::setSourceLayer(const std::string& sourceLayer) { - impl->sourceLayer = sourceLayer; + auto impl_ = mutableImpl(); + impl_->sourceLayer = sourceLayer; + baseImpl = std::move(impl_); } const std::string& CircleLayer::getSourceLayer() const { - return impl->sourceLayer; + return impl().sourceLayer; } // Filter void CircleLayer::setFilter(const Filter& filter) { - impl->filter = filter; - impl->observer->onLayerFilterChanged(*this); + auto impl_ = mutableImpl(); + impl_->filter = filter; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } const Filter& CircleLayer::getFilter() const { - return impl->filter; + return impl().filter; +} + +// Visibility + +void CircleLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void CircleLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void CircleLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); } // Layout properties @@ -69,258 +98,297 @@ DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleRadius() { return { 5 }; } -DataDrivenPropertyValue<float> CircleLayer::getCircleRadius(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleRadius>().get(klass); +DataDrivenPropertyValue<float> CircleLayer::getCircleRadius() const { + return impl().paint.template get<CircleRadius>().value; } -void CircleLayer::setCircleRadius(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getCircleRadius(klass)) +void CircleLayer::setCircleRadius(DataDrivenPropertyValue<float> value) { + if (value == getCircleRadius()) return; - impl->cascading.template get<CircleRadius>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleRadius>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleRadiusTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleRadius>().setTransition(value, klass); +void CircleLayer::setCircleRadiusTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleRadius>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleRadiusTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleRadius>().getTransition(klass); +TransitionOptions CircleLayer::getCircleRadiusTransition() const { + return impl().paint.template get<CircleRadius>().options; } DataDrivenPropertyValue<Color> CircleLayer::getDefaultCircleColor() { return { Color::black() }; } -DataDrivenPropertyValue<Color> CircleLayer::getCircleColor(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleColor>().get(klass); +DataDrivenPropertyValue<Color> CircleLayer::getCircleColor() const { + return impl().paint.template get<CircleColor>().value; } -void CircleLayer::setCircleColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getCircleColor(klass)) +void CircleLayer::setCircleColor(DataDrivenPropertyValue<Color> value) { + if (value == getCircleColor()) return; - impl->cascading.template get<CircleColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleColor>().setTransition(value, klass); +void CircleLayer::setCircleColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleColor>().getTransition(klass); +TransitionOptions CircleLayer::getCircleColorTransition() const { + return impl().paint.template get<CircleColor>().options; } DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleBlur() { return { 0 }; } -DataDrivenPropertyValue<float> CircleLayer::getCircleBlur(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleBlur>().get(klass); +DataDrivenPropertyValue<float> CircleLayer::getCircleBlur() const { + return impl().paint.template get<CircleBlur>().value; } -void CircleLayer::setCircleBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getCircleBlur(klass)) +void CircleLayer::setCircleBlur(DataDrivenPropertyValue<float> value) { + if (value == getCircleBlur()) return; - impl->cascading.template get<CircleBlur>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleBlur>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleBlur>().setTransition(value, klass); +void CircleLayer::setCircleBlurTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleBlur>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleBlurTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleBlur>().getTransition(klass); +TransitionOptions CircleLayer::getCircleBlurTransition() const { + return impl().paint.template get<CircleBlur>().options; } DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleOpacity() { return { 1 }; } -DataDrivenPropertyValue<float> CircleLayer::getCircleOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleOpacity>().get(klass); +DataDrivenPropertyValue<float> CircleLayer::getCircleOpacity() const { + return impl().paint.template get<CircleOpacity>().value; } -void CircleLayer::setCircleOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getCircleOpacity(klass)) +void CircleLayer::setCircleOpacity(DataDrivenPropertyValue<float> value) { + if (value == getCircleOpacity()) return; - impl->cascading.template get<CircleOpacity>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleOpacity>().setTransition(value, klass); +void CircleLayer::setCircleOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleOpacity>().getTransition(klass); +TransitionOptions CircleLayer::getCircleOpacityTransition() const { + return impl().paint.template get<CircleOpacity>().options; } PropertyValue<std::array<float, 2>> CircleLayer::getDefaultCircleTranslate() { return { {{ 0, 0 }} }; } -PropertyValue<std::array<float, 2>> CircleLayer::getCircleTranslate(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleTranslate>().get(klass); +PropertyValue<std::array<float, 2>> CircleLayer::getCircleTranslate() const { + return impl().paint.template get<CircleTranslate>().value; } -void CircleLayer::setCircleTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) { - if (value == getCircleTranslate(klass)) +void CircleLayer::setCircleTranslate(PropertyValue<std::array<float, 2>> value) { + if (value == getCircleTranslate()) return; - impl->cascading.template get<CircleTranslate>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleTranslate>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleTranslate>().setTransition(value, klass); +void CircleLayer::setCircleTranslateTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleTranslate>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleTranslateTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleTranslate>().getTransition(klass); +TransitionOptions CircleLayer::getCircleTranslateTransition() const { + return impl().paint.template get<CircleTranslate>().options; } PropertyValue<TranslateAnchorType> CircleLayer::getDefaultCircleTranslateAnchor() { return { TranslateAnchorType::Map }; } -PropertyValue<TranslateAnchorType> CircleLayer::getCircleTranslateAnchor(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleTranslateAnchor>().get(klass); +PropertyValue<TranslateAnchorType> CircleLayer::getCircleTranslateAnchor() const { + return impl().paint.template get<CircleTranslateAnchor>().value; } -void CircleLayer::setCircleTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) { - if (value == getCircleTranslateAnchor(klass)) +void CircleLayer::setCircleTranslateAnchor(PropertyValue<TranslateAnchorType> value) { + if (value == getCircleTranslateAnchor()) return; - impl->cascading.template get<CircleTranslateAnchor>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleTranslateAnchor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleTranslateAnchor>().setTransition(value, klass); +void CircleLayer::setCircleTranslateAnchorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleTranslateAnchor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleTranslateAnchorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleTranslateAnchor>().getTransition(klass); +TransitionOptions CircleLayer::getCircleTranslateAnchorTransition() const { + return impl().paint.template get<CircleTranslateAnchor>().options; } PropertyValue<CirclePitchScaleType> CircleLayer::getDefaultCirclePitchScale() { return { CirclePitchScaleType::Map }; } -PropertyValue<CirclePitchScaleType> CircleLayer::getCirclePitchScale(const optional<std::string>& klass) const { - return impl->cascading.template get<CirclePitchScale>().get(klass); +PropertyValue<CirclePitchScaleType> CircleLayer::getCirclePitchScale() const { + return impl().paint.template get<CirclePitchScale>().value; +} + +void CircleLayer::setCirclePitchScale(PropertyValue<CirclePitchScaleType> value) { + if (value == getCirclePitchScale()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get<CirclePitchScale>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void CircleLayer::setCirclePitchScaleTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CirclePitchScale>().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions CircleLayer::getCirclePitchScaleTransition() const { + return impl().paint.template get<CirclePitchScale>().options; +} + +PropertyValue<AlignmentType> CircleLayer::getDefaultCirclePitchAlignment() { + return { AlignmentType::Viewport }; +} + +PropertyValue<AlignmentType> CircleLayer::getCirclePitchAlignment() const { + return impl().paint.template get<CirclePitchAlignment>().value; } -void CircleLayer::setCirclePitchScale(PropertyValue<CirclePitchScaleType> value, const optional<std::string>& klass) { - if (value == getCirclePitchScale(klass)) +void CircleLayer::setCirclePitchAlignment(PropertyValue<AlignmentType> value) { + if (value == getCirclePitchAlignment()) return; - impl->cascading.template get<CirclePitchScale>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<CirclePitchAlignment>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCirclePitchScaleTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CirclePitchScale>().setTransition(value, klass); +void CircleLayer::setCirclePitchAlignmentTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CirclePitchAlignment>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCirclePitchScaleTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CirclePitchScale>().getTransition(klass); +TransitionOptions CircleLayer::getCirclePitchAlignmentTransition() const { + return impl().paint.template get<CirclePitchAlignment>().options; } DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleStrokeWidth() { return { 0 }; } -DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeWidth(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleStrokeWidth>().get(klass); +DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeWidth() const { + return impl().paint.template get<CircleStrokeWidth>().value; } -void CircleLayer::setCircleStrokeWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getCircleStrokeWidth(klass)) +void CircleLayer::setCircleStrokeWidth(DataDrivenPropertyValue<float> value) { + if (value == getCircleStrokeWidth()) return; - impl->cascading.template get<CircleStrokeWidth>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleStrokeWidth>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleStrokeWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleStrokeWidth>().setTransition(value, klass); +void CircleLayer::setCircleStrokeWidthTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleStrokeWidth>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleStrokeWidthTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleStrokeWidth>().getTransition(klass); +TransitionOptions CircleLayer::getCircleStrokeWidthTransition() const { + return impl().paint.template get<CircleStrokeWidth>().options; } DataDrivenPropertyValue<Color> CircleLayer::getDefaultCircleStrokeColor() { return { Color::black() }; } -DataDrivenPropertyValue<Color> CircleLayer::getCircleStrokeColor(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleStrokeColor>().get(klass); +DataDrivenPropertyValue<Color> CircleLayer::getCircleStrokeColor() const { + return impl().paint.template get<CircleStrokeColor>().value; } -void CircleLayer::setCircleStrokeColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getCircleStrokeColor(klass)) +void CircleLayer::setCircleStrokeColor(DataDrivenPropertyValue<Color> value) { + if (value == getCircleStrokeColor()) return; - impl->cascading.template get<CircleStrokeColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleStrokeColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleStrokeColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleStrokeColor>().setTransition(value, klass); +void CircleLayer::setCircleStrokeColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleStrokeColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleStrokeColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleStrokeColor>().getTransition(klass); +TransitionOptions CircleLayer::getCircleStrokeColorTransition() const { + return impl().paint.template get<CircleStrokeColor>().options; } DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleStrokeOpacity() { return { 1 }; } -DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleStrokeOpacity>().get(klass); +DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeOpacity() const { + return impl().paint.template get<CircleStrokeOpacity>().value; } -void CircleLayer::setCircleStrokeOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getCircleStrokeOpacity(klass)) +void CircleLayer::setCircleStrokeOpacity(DataDrivenPropertyValue<float> value) { + if (value == getCircleStrokeOpacity()) return; - impl->cascading.template get<CircleStrokeOpacity>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleStrokeOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void CircleLayer::setCircleStrokeOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<CircleStrokeOpacity>().setTransition(value, klass); +void CircleLayer::setCircleStrokeOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<CircleStrokeOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions CircleLayer::getCircleStrokeOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<CircleStrokeOpacity>().getTransition(klass); +TransitionOptions CircleLayer::getCircleStrokeOpacityTransition() const { + return impl().paint.template get<CircleStrokeOpacity>().options; } } // namespace style diff --git a/src/mbgl/style/layers/circle_layer_impl.cpp b/src/mbgl/style/layers/circle_layer_impl.cpp index 31b286f273..69f574cd6b 100644 --- a/src/mbgl/style/layers/circle_layer_impl.cpp +++ b/src/mbgl/style/layers/circle_layer_impl.cpp @@ -1,11 +1,14 @@ #include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/renderer/render_circle_layer.hpp> namespace mbgl { namespace style { -std::unique_ptr<RenderLayer> CircleLayer::Impl::createRenderLayer() const { - return std::make_unique<RenderCircleLayer>(*this); +bool CircleLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { + assert(dynamic_cast<const CircleLayer::Impl*>(&other)); + const auto& impl = static_cast<const style::CircleLayer::Impl&>(other); + return filter != impl.filter || + visibility != impl.visibility || + paint.hasDataDrivenPropertyDifference(impl.paint); } } // namespace style diff --git a/src/mbgl/style/layers/circle_layer_impl.hpp b/src/mbgl/style/layers/circle_layer_impl.hpp index 886815f0d1..4b148cdc42 100644 --- a/src/mbgl/style/layers/circle_layer_impl.hpp +++ b/src/mbgl/style/layers/circle_layer_impl.hpp @@ -9,13 +9,12 @@ namespace style { class CircleLayer::Impl : public Layer::Impl { public: - std::unique_ptr<Layer> clone() const override; - std::unique_ptr<Layer> cloneRef(const std::string& id) const override; - void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; + using Layer::Impl::Impl; - std::unique_ptr<RenderLayer> createRenderLayer() const override; + bool hasLayoutDifference(const Layer::Impl&) const override; + void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - CirclePaintProperties::Cascading cascading; + CirclePaintProperties::Transitionable paint; }; } // namespace style diff --git a/src/mbgl/style/layers/circle_layer_properties.hpp b/src/mbgl/style/layers/circle_layer_properties.hpp index 58206c61da..bc0c961e75 100644 --- a/src/mbgl/style/layers/circle_layer_properties.hpp +++ b/src/mbgl/style/layers/circle_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> @@ -39,6 +40,10 @@ struct CirclePitchScale : PaintProperty<CirclePitchScaleType> { static CirclePitchScaleType defaultValue() { return CirclePitchScaleType::Map; } }; +struct CirclePitchAlignment : PaintProperty<AlignmentType> { + static AlignmentType defaultValue() { return AlignmentType::Viewport; } +}; + struct CircleStrokeWidth : DataDrivenPaintProperty<float, attributes::a_stroke_width, uniforms::u_stroke_width> { static float defaultValue() { return 0; } }; @@ -51,7 +56,7 @@ struct CircleStrokeOpacity : DataDrivenPaintProperty<float, attributes::a_stroke static float defaultValue() { return 1; } }; -class CirclePaintProperties : public PaintProperties< +class CirclePaintProperties : public Properties< CircleRadius, CircleColor, CircleBlur, @@ -59,6 +64,7 @@ class CirclePaintProperties : public PaintProperties< CircleTranslate, CircleTranslateAnchor, CirclePitchScale, + CirclePitchAlignment, CircleStrokeWidth, CircleStrokeColor, CircleStrokeOpacity diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp index cda8a157f0..854c771847 100644 --- a/src/mbgl/style/layers/custom_layer.cpp +++ b/src/mbgl/style/layers/custom_layer.cpp @@ -1,6 +1,6 @@ #include <mbgl/style/layers/custom_layer.hpp> #include <mbgl/style/layers/custom_layer_impl.hpp> -#include <mbgl/util/logging.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { @@ -8,23 +8,63 @@ namespace style { CustomLayer::CustomLayer(const std::string& layerID, CustomLayerInitializeFunction init, CustomLayerRenderFunction render, + CustomLayerContextLostFunction contextLost, CustomLayerDeinitializeFunction deinit, void* context) - : Layer(LayerType::Custom, std::make_unique<Impl>(layerID, init, render, deinit, context)) - , impl(static_cast<Impl*>(baseImpl.get())) { - Log::Info(Event::General, "New custom layer: %s", layerID.c_str()); + : Layer(makeMutable<Impl>(layerID, init, render, contextLost, deinit, context)) { } -CustomLayer::CustomLayer(const Impl& other) - : Layer(LayerType::Custom, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +CustomLayer::CustomLayer(const std::string& layerID, + CustomLayerInitializeFunction init, + CustomLayerRenderFunction render, + CustomLayerDeinitializeFunction deinit, + void* context) + : Layer(makeMutable<Impl>(layerID, init, render, nullptr, deinit, context)) { } CustomLayer::~CustomLayer() = default; +const CustomLayer::Impl& CustomLayer::impl() const { + return static_cast<const Impl&>(*baseImpl); +} + +Mutable<CustomLayer::Impl> CustomLayer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> CustomLayer::cloneRef(const std::string&) const { + assert(false); + return nullptr; +} + +// Visibility + +void CustomLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void CustomLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void CustomLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); +} + template <> bool Layer::is<CustomLayer>() const { - return type == LayerType::Custom; + return getType() == LayerType::Custom; } } // namespace style diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp index 1d3e9af8d6..1de268d2e2 100644 --- a/src/mbgl/style/layers/custom_layer_impl.cpp +++ b/src/mbgl/style/layers/custom_layer_impl.cpp @@ -1,74 +1,28 @@ #include <mbgl/style/layers/custom_layer_impl.hpp> -#include <mbgl/renderer/render_custom_layer.hpp> -#include <mbgl/map/transform_state.hpp> -#include <mbgl/util/logging.hpp> + namespace mbgl { namespace style { -std::unique_ptr<RenderLayer> CustomLayer::Impl::createRenderLayer() const { - return std::make_unique<RenderCustomLayer>(*this); -} - CustomLayer::Impl::Impl(const std::string& id_, CustomLayerInitializeFunction initializeFn_, CustomLayerRenderFunction renderFn_, + CustomLayerContextLostFunction contextLostFn_, CustomLayerDeinitializeFunction deinitializeFn_, - void* context_) { - Log::Info(Event::General, "New custom layer Impl: %s", id_.c_str()); - id = id_; + void* context_) + : Layer::Impl(LayerType::Custom, id_, std::string()) { initializeFn = initializeFn_; renderFn = renderFn_; deinitializeFn = deinitializeFn_; + contextLostFn = contextLostFn_; context = context_; } -CustomLayer::Impl::Impl(const CustomLayer::Impl &other) - : Layer::Impl(other) { - id = other.id; - // Don't copy anything else. -} - -CustomLayer::Impl::~Impl() = default; - -std::unique_ptr<Layer> CustomLayer::Impl::clone() const { - return std::make_unique<CustomLayer>(*this); -} - -std::unique_ptr<Layer> CustomLayer::Impl::cloneRef(const std::string&) const { - assert(false); - return std::make_unique<CustomLayer>(*this); +bool CustomLayer::Impl::hasLayoutDifference(const Layer::Impl&) const { + return false; } void CustomLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const { } -void CustomLayer::Impl::initialize() { - assert(initializeFn); - initializeFn(context); -} - -void CustomLayer::Impl::deinitialize() { - if (deinitializeFn) { - deinitializeFn(context); - } -} - -void CustomLayer::Impl::render(const TransformState& state) const { - assert(renderFn); - - CustomLayerRenderParameters parameters; - - parameters.width = state.getSize().width; - parameters.height = state.getSize().height; - parameters.latitude = state.getLatLng().latitude(); - parameters.longitude = state.getLatLng().longitude(); - parameters.zoom = state.getZoom(); - parameters.bearing = -state.getAngle() * util::RAD2DEG; - parameters.pitch = state.getPitch(); - parameters.fieldOfView = state.getFieldOfView(); - - renderFn(context, parameters); -} - } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp index e612d17f14..62efbbe15b 100644 --- a/src/mbgl/style/layers/custom_layer_impl.hpp +++ b/src/mbgl/style/layers/custom_layer_impl.hpp @@ -14,25 +14,16 @@ public: Impl(const std::string& id, CustomLayerInitializeFunction, CustomLayerRenderFunction, + CustomLayerContextLostFunction, CustomLayerDeinitializeFunction, void* context); - Impl(const Impl&); - ~Impl() final; - - void initialize(); - void deinitialize(); - void render(const TransformState&) const; - -private: - std::unique_ptr<Layer> clone() const override; - std::unique_ptr<Layer> cloneRef(const std::string& id) const override; + bool hasLayoutDifference(const Layer::Impl&) const override; void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - std::unique_ptr<RenderLayer> createRenderLayer() const final; - CustomLayerInitializeFunction initializeFn = nullptr; CustomLayerRenderFunction renderFn = nullptr; + CustomLayerContextLostFunction contextLostFn = nullptr; CustomLayerDeinitializeFunction deinitializeFn = nullptr; void* context = nullptr; }; diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp index 6f11d6052c..62f92cef75 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp @@ -2,34 +2,34 @@ #include <mbgl/style/layers/fill_extrusion_layer.hpp> #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> -#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { FillExtrusionLayer::FillExtrusionLayer(const std::string& layerID, const std::string& sourceID) - : Layer(LayerType::FillExtrusion, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; - impl->source = sourceID; + : Layer(makeMutable<Impl>(LayerType::FillExtrusion, layerID, sourceID)) { } -FillExtrusionLayer::FillExtrusionLayer(const Impl& other) - : Layer(LayerType::FillExtrusion, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +FillExtrusionLayer::FillExtrusionLayer(Immutable<Impl> impl_) + : Layer(std::move(impl_)) { } FillExtrusionLayer::~FillExtrusionLayer() = default; -std::unique_ptr<Layer> FillExtrusionLayer::Impl::clone() const { - return std::make_unique<FillExtrusionLayer>(*this); +const FillExtrusionLayer::Impl& FillExtrusionLayer::impl() const { + return static_cast<const Impl&>(*baseImpl); } -std::unique_ptr<Layer> FillExtrusionLayer::Impl::cloneRef(const std::string& id_) const { - auto result = std::make_unique<FillExtrusionLayer>(*this); - result->impl->id = id_; - result->impl->cascading = FillExtrusionPaintProperties::Cascading(); - return std::move(result); +Mutable<FillExtrusionLayer::Impl> FillExtrusionLayer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> FillExtrusionLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = FillExtrusionPaintProperties::Transitionable(); + return std::make_unique<FillExtrusionLayer>(std::move(impl_)); } void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const { @@ -38,26 +38,55 @@ void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::Stri // Source const std::string& FillExtrusionLayer::getSourceID() const { - return impl->source; + return impl().source; } void FillExtrusionLayer::setSourceLayer(const std::string& sourceLayer) { - impl->sourceLayer = sourceLayer; + auto impl_ = mutableImpl(); + impl_->sourceLayer = sourceLayer; + baseImpl = std::move(impl_); } const std::string& FillExtrusionLayer::getSourceLayer() const { - return impl->sourceLayer; + return impl().sourceLayer; } // Filter void FillExtrusionLayer::setFilter(const Filter& filter) { - impl->filter = filter; - impl->observer->onLayerFilterChanged(*this); + auto impl_ = mutableImpl(); + impl_->filter = filter; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } const Filter& FillExtrusionLayer::getFilter() const { - return impl->filter; + return impl().filter; +} + +// Visibility + +void FillExtrusionLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void FillExtrusionLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void FillExtrusionLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); } // Layout properties @@ -69,173 +98,189 @@ PropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionOpacity() { return { 1 }; } -PropertyValue<float> FillExtrusionLayer::getFillExtrusionOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionOpacity>().get(klass); +PropertyValue<float> FillExtrusionLayer::getFillExtrusionOpacity() const { + return impl().paint.template get<FillExtrusionOpacity>().value; } -void FillExtrusionLayer::setFillExtrusionOpacity(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getFillExtrusionOpacity(klass)) +void FillExtrusionLayer::setFillExtrusionOpacity(PropertyValue<float> value) { + if (value == getFillExtrusionOpacity()) return; - impl->cascading.template get<FillExtrusionOpacity>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillExtrusionLayer::setFillExtrusionOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillExtrusionOpacity>().setTransition(value, klass); +void FillExtrusionLayer::setFillExtrusionOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillExtrusionLayer::getFillExtrusionOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionOpacity>().getTransition(klass); +TransitionOptions FillExtrusionLayer::getFillExtrusionOpacityTransition() const { + return impl().paint.template get<FillExtrusionOpacity>().options; } DataDrivenPropertyValue<Color> FillExtrusionLayer::getDefaultFillExtrusionColor() { return { Color::black() }; } -DataDrivenPropertyValue<Color> FillExtrusionLayer::getFillExtrusionColor(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionColor>().get(klass); +DataDrivenPropertyValue<Color> FillExtrusionLayer::getFillExtrusionColor() const { + return impl().paint.template get<FillExtrusionColor>().value; } -void FillExtrusionLayer::setFillExtrusionColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getFillExtrusionColor(klass)) +void FillExtrusionLayer::setFillExtrusionColor(DataDrivenPropertyValue<Color> value) { + if (value == getFillExtrusionColor()) return; - impl->cascading.template get<FillExtrusionColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillExtrusionLayer::setFillExtrusionColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillExtrusionColor>().setTransition(value, klass); +void FillExtrusionLayer::setFillExtrusionColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillExtrusionLayer::getFillExtrusionColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionColor>().getTransition(klass); +TransitionOptions FillExtrusionLayer::getFillExtrusionColorTransition() const { + return impl().paint.template get<FillExtrusionColor>().options; } PropertyValue<std::array<float, 2>> FillExtrusionLayer::getDefaultFillExtrusionTranslate() { return { {{ 0, 0 }} }; } -PropertyValue<std::array<float, 2>> FillExtrusionLayer::getFillExtrusionTranslate(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionTranslate>().get(klass); +PropertyValue<std::array<float, 2>> FillExtrusionLayer::getFillExtrusionTranslate() const { + return impl().paint.template get<FillExtrusionTranslate>().value; } -void FillExtrusionLayer::setFillExtrusionTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) { - if (value == getFillExtrusionTranslate(klass)) +void FillExtrusionLayer::setFillExtrusionTranslate(PropertyValue<std::array<float, 2>> value) { + if (value == getFillExtrusionTranslate()) return; - impl->cascading.template get<FillExtrusionTranslate>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionTranslate>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillExtrusionLayer::setFillExtrusionTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillExtrusionTranslate>().setTransition(value, klass); +void FillExtrusionLayer::setFillExtrusionTranslateTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionTranslate>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillExtrusionLayer::getFillExtrusionTranslateTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionTranslate>().getTransition(klass); +TransitionOptions FillExtrusionLayer::getFillExtrusionTranslateTransition() const { + return impl().paint.template get<FillExtrusionTranslate>().options; } PropertyValue<TranslateAnchorType> FillExtrusionLayer::getDefaultFillExtrusionTranslateAnchor() { return { TranslateAnchorType::Map }; } -PropertyValue<TranslateAnchorType> FillExtrusionLayer::getFillExtrusionTranslateAnchor(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionTranslateAnchor>().get(klass); +PropertyValue<TranslateAnchorType> FillExtrusionLayer::getFillExtrusionTranslateAnchor() const { + return impl().paint.template get<FillExtrusionTranslateAnchor>().value; } -void FillExtrusionLayer::setFillExtrusionTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) { - if (value == getFillExtrusionTranslateAnchor(klass)) +void FillExtrusionLayer::setFillExtrusionTranslateAnchor(PropertyValue<TranslateAnchorType> value) { + if (value == getFillExtrusionTranslateAnchor()) return; - impl->cascading.template get<FillExtrusionTranslateAnchor>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionTranslateAnchor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillExtrusionTranslateAnchor>().setTransition(value, klass); +void FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionTranslateAnchor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillExtrusionLayer::getFillExtrusionTranslateAnchorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionTranslateAnchor>().getTransition(klass); +TransitionOptions FillExtrusionLayer::getFillExtrusionTranslateAnchorTransition() const { + return impl().paint.template get<FillExtrusionTranslateAnchor>().options; } PropertyValue<std::string> FillExtrusionLayer::getDefaultFillExtrusionPattern() { return { "" }; } -PropertyValue<std::string> FillExtrusionLayer::getFillExtrusionPattern(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionPattern>().get(klass); +PropertyValue<std::string> FillExtrusionLayer::getFillExtrusionPattern() const { + return impl().paint.template get<FillExtrusionPattern>().value; } -void FillExtrusionLayer::setFillExtrusionPattern(PropertyValue<std::string> value, const optional<std::string>& klass) { - if (value == getFillExtrusionPattern(klass)) +void FillExtrusionLayer::setFillExtrusionPattern(PropertyValue<std::string> value) { + if (value == getFillExtrusionPattern()) return; - impl->cascading.template get<FillExtrusionPattern>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionPattern>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillExtrusionLayer::setFillExtrusionPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillExtrusionPattern>().setTransition(value, klass); +void FillExtrusionLayer::setFillExtrusionPatternTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionPattern>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillExtrusionLayer::getFillExtrusionPatternTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionPattern>().getTransition(klass); +TransitionOptions FillExtrusionLayer::getFillExtrusionPatternTransition() const { + return impl().paint.template get<FillExtrusionPattern>().options; } DataDrivenPropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionHeight() { return { 0 }; } -DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionHeight(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionHeight>().get(klass); +DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionHeight() const { + return impl().paint.template get<FillExtrusionHeight>().value; } -void FillExtrusionLayer::setFillExtrusionHeight(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getFillExtrusionHeight(klass)) +void FillExtrusionLayer::setFillExtrusionHeight(DataDrivenPropertyValue<float> value) { + if (value == getFillExtrusionHeight()) return; - impl->cascading.template get<FillExtrusionHeight>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionHeight>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillExtrusionLayer::setFillExtrusionHeightTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillExtrusionHeight>().setTransition(value, klass); +void FillExtrusionLayer::setFillExtrusionHeightTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionHeight>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillExtrusionLayer::getFillExtrusionHeightTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionHeight>().getTransition(klass); +TransitionOptions FillExtrusionLayer::getFillExtrusionHeightTransition() const { + return impl().paint.template get<FillExtrusionHeight>().options; } DataDrivenPropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionBase() { return { 0 }; } -DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionBase(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionBase>().get(klass); +DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionBase() const { + return impl().paint.template get<FillExtrusionBase>().value; } -void FillExtrusionLayer::setFillExtrusionBase(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getFillExtrusionBase(klass)) +void FillExtrusionLayer::setFillExtrusionBase(DataDrivenPropertyValue<float> value) { + if (value == getFillExtrusionBase()) return; - impl->cascading.template get<FillExtrusionBase>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionBase>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillExtrusionLayer::setFillExtrusionBaseTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillExtrusionBase>().setTransition(value, klass); +void FillExtrusionLayer::setFillExtrusionBaseTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillExtrusionBase>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillExtrusionLayer::getFillExtrusionBaseTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillExtrusionBase>().getTransition(klass); +TransitionOptions FillExtrusionLayer::getFillExtrusionBaseTransition() const { + return impl().paint.template get<FillExtrusionBase>().options; } } // namespace style diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp index 5340541221..d37c2ad29b 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp @@ -1,11 +1,14 @@ #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> namespace mbgl { namespace style { -std::unique_ptr<RenderLayer> FillExtrusionLayer::Impl::createRenderLayer() const { - return std::make_unique<RenderFillExtrusionLayer>(*this); +bool FillExtrusionLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { + assert(dynamic_cast<const FillExtrusionLayer::Impl*>(&other)); + const auto& impl = static_cast<const style::FillExtrusionLayer::Impl&>(other); + return filter != impl.filter || + visibility != impl.visibility || + paint.hasDataDrivenPropertyDifference(impl.paint); } } // namespace style diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp index 2353bd99fe..9abc6fc4b3 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp @@ -9,13 +9,12 @@ namespace style { class FillExtrusionLayer::Impl : public Layer::Impl { public: - std::unique_ptr<Layer> clone() const override; - std::unique_ptr<Layer> cloneRef(const std::string& id) const override; - void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; + using Layer::Impl::Impl; - std::unique_ptr<RenderLayer> createRenderLayer() const override; + bool hasLayoutDifference(const Layer::Impl&) const override; + void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - FillExtrusionPaintProperties::Cascading cascading; + FillExtrusionPaintProperties::Transitionable paint; }; } // namespace style diff --git a/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp b/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp index f41ce68b94..19be59a2fe 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> @@ -39,7 +40,7 @@ struct FillExtrusionBase : DataDrivenPaintProperty<float, attributes::a_base, un static float defaultValue() { return 0; } }; -class FillExtrusionPaintProperties : public PaintProperties< +class FillExtrusionPaintProperties : public Properties< FillExtrusionOpacity, FillExtrusionColor, FillExtrusionTranslate, diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp index 9fd9d33af3..65975752db 100644 --- a/src/mbgl/style/layers/fill_layer.cpp +++ b/src/mbgl/style/layers/fill_layer.cpp @@ -2,34 +2,34 @@ #include <mbgl/style/layers/fill_layer.hpp> #include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { FillLayer::FillLayer(const std::string& layerID, const std::string& sourceID) - : Layer(LayerType::Fill, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; - impl->source = sourceID; + : Layer(makeMutable<Impl>(LayerType::Fill, layerID, sourceID)) { } -FillLayer::FillLayer(const Impl& other) - : Layer(LayerType::Fill, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +FillLayer::FillLayer(Immutable<Impl> impl_) + : Layer(std::move(impl_)) { } FillLayer::~FillLayer() = default; -std::unique_ptr<Layer> FillLayer::Impl::clone() const { - return std::make_unique<FillLayer>(*this); +const FillLayer::Impl& FillLayer::impl() const { + return static_cast<const Impl&>(*baseImpl); } -std::unique_ptr<Layer> FillLayer::Impl::cloneRef(const std::string& id_) const { - auto result = std::make_unique<FillLayer>(*this); - result->impl->id = id_; - result->impl->cascading = FillPaintProperties::Cascading(); - return std::move(result); +Mutable<FillLayer::Impl> FillLayer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> FillLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = FillPaintProperties::Transitionable(); + return std::make_unique<FillLayer>(std::move(impl_)); } void FillLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const { @@ -38,26 +38,55 @@ void FillLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer> // Source const std::string& FillLayer::getSourceID() const { - return impl->source; + return impl().source; } void FillLayer::setSourceLayer(const std::string& sourceLayer) { - impl->sourceLayer = sourceLayer; + auto impl_ = mutableImpl(); + impl_->sourceLayer = sourceLayer; + baseImpl = std::move(impl_); } const std::string& FillLayer::getSourceLayer() const { - return impl->sourceLayer; + return impl().sourceLayer; } // Filter void FillLayer::setFilter(const Filter& filter) { - impl->filter = filter; - impl->observer->onLayerFilterChanged(*this); + auto impl_ = mutableImpl(); + impl_->filter = filter; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } const Filter& FillLayer::getFilter() const { - return impl->filter; + return impl().filter; +} + +// Visibility + +void FillLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void FillLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void FillLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); } // Layout properties @@ -69,173 +98,189 @@ PropertyValue<bool> FillLayer::getDefaultFillAntialias() { return { true }; } -PropertyValue<bool> FillLayer::getFillAntialias(const optional<std::string>& klass) const { - return impl->cascading.template get<FillAntialias>().get(klass); +PropertyValue<bool> FillLayer::getFillAntialias() const { + return impl().paint.template get<FillAntialias>().value; } -void FillLayer::setFillAntialias(PropertyValue<bool> value, const optional<std::string>& klass) { - if (value == getFillAntialias(klass)) +void FillLayer::setFillAntialias(PropertyValue<bool> value) { + if (value == getFillAntialias()) return; - impl->cascading.template get<FillAntialias>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<FillAntialias>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillLayer::setFillAntialiasTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillAntialias>().setTransition(value, klass); +void FillLayer::setFillAntialiasTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillAntialias>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillLayer::getFillAntialiasTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillAntialias>().getTransition(klass); +TransitionOptions FillLayer::getFillAntialiasTransition() const { + return impl().paint.template get<FillAntialias>().options; } DataDrivenPropertyValue<float> FillLayer::getDefaultFillOpacity() { return { 1 }; } -DataDrivenPropertyValue<float> FillLayer::getFillOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<FillOpacity>().get(klass); +DataDrivenPropertyValue<float> FillLayer::getFillOpacity() const { + return impl().paint.template get<FillOpacity>().value; } -void FillLayer::setFillOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getFillOpacity(klass)) +void FillLayer::setFillOpacity(DataDrivenPropertyValue<float> value) { + if (value == getFillOpacity()) return; - impl->cascading.template get<FillOpacity>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<FillOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillLayer::setFillOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillOpacity>().setTransition(value, klass); +void FillLayer::setFillOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillLayer::getFillOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillOpacity>().getTransition(klass); +TransitionOptions FillLayer::getFillOpacityTransition() const { + return impl().paint.template get<FillOpacity>().options; } DataDrivenPropertyValue<Color> FillLayer::getDefaultFillColor() { return { Color::black() }; } -DataDrivenPropertyValue<Color> FillLayer::getFillColor(const optional<std::string>& klass) const { - return impl->cascading.template get<FillColor>().get(klass); +DataDrivenPropertyValue<Color> FillLayer::getFillColor() const { + return impl().paint.template get<FillColor>().value; } -void FillLayer::setFillColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getFillColor(klass)) +void FillLayer::setFillColor(DataDrivenPropertyValue<Color> value) { + if (value == getFillColor()) return; - impl->cascading.template get<FillColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<FillColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillLayer::setFillColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillColor>().setTransition(value, klass); +void FillLayer::setFillColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillLayer::getFillColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillColor>().getTransition(klass); +TransitionOptions FillLayer::getFillColorTransition() const { + return impl().paint.template get<FillColor>().options; } DataDrivenPropertyValue<Color> FillLayer::getDefaultFillOutlineColor() { return { {} }; } -DataDrivenPropertyValue<Color> FillLayer::getFillOutlineColor(const optional<std::string>& klass) const { - return impl->cascading.template get<FillOutlineColor>().get(klass); +DataDrivenPropertyValue<Color> FillLayer::getFillOutlineColor() const { + return impl().paint.template get<FillOutlineColor>().value; } -void FillLayer::setFillOutlineColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getFillOutlineColor(klass)) +void FillLayer::setFillOutlineColor(DataDrivenPropertyValue<Color> value) { + if (value == getFillOutlineColor()) return; - impl->cascading.template get<FillOutlineColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<FillOutlineColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillLayer::setFillOutlineColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillOutlineColor>().setTransition(value, klass); +void FillLayer::setFillOutlineColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillOutlineColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillLayer::getFillOutlineColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillOutlineColor>().getTransition(klass); +TransitionOptions FillLayer::getFillOutlineColorTransition() const { + return impl().paint.template get<FillOutlineColor>().options; } PropertyValue<std::array<float, 2>> FillLayer::getDefaultFillTranslate() { return { {{ 0, 0 }} }; } -PropertyValue<std::array<float, 2>> FillLayer::getFillTranslate(const optional<std::string>& klass) const { - return impl->cascading.template get<FillTranslate>().get(klass); +PropertyValue<std::array<float, 2>> FillLayer::getFillTranslate() const { + return impl().paint.template get<FillTranslate>().value; } -void FillLayer::setFillTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) { - if (value == getFillTranslate(klass)) +void FillLayer::setFillTranslate(PropertyValue<std::array<float, 2>> value) { + if (value == getFillTranslate()) return; - impl->cascading.template get<FillTranslate>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<FillTranslate>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillLayer::setFillTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillTranslate>().setTransition(value, klass); +void FillLayer::setFillTranslateTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillTranslate>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillLayer::getFillTranslateTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillTranslate>().getTransition(klass); +TransitionOptions FillLayer::getFillTranslateTransition() const { + return impl().paint.template get<FillTranslate>().options; } PropertyValue<TranslateAnchorType> FillLayer::getDefaultFillTranslateAnchor() { return { TranslateAnchorType::Map }; } -PropertyValue<TranslateAnchorType> FillLayer::getFillTranslateAnchor(const optional<std::string>& klass) const { - return impl->cascading.template get<FillTranslateAnchor>().get(klass); +PropertyValue<TranslateAnchorType> FillLayer::getFillTranslateAnchor() const { + return impl().paint.template get<FillTranslateAnchor>().value; } -void FillLayer::setFillTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) { - if (value == getFillTranslateAnchor(klass)) +void FillLayer::setFillTranslateAnchor(PropertyValue<TranslateAnchorType> value) { + if (value == getFillTranslateAnchor()) return; - impl->cascading.template get<FillTranslateAnchor>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<FillTranslateAnchor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillLayer::setFillTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillTranslateAnchor>().setTransition(value, klass); +void FillLayer::setFillTranslateAnchorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillTranslateAnchor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillLayer::getFillTranslateAnchorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillTranslateAnchor>().getTransition(klass); +TransitionOptions FillLayer::getFillTranslateAnchorTransition() const { + return impl().paint.template get<FillTranslateAnchor>().options; } PropertyValue<std::string> FillLayer::getDefaultFillPattern() { return { "" }; } -PropertyValue<std::string> FillLayer::getFillPattern(const optional<std::string>& klass) const { - return impl->cascading.template get<FillPattern>().get(klass); +PropertyValue<std::string> FillLayer::getFillPattern() const { + return impl().paint.template get<FillPattern>().value; } -void FillLayer::setFillPattern(PropertyValue<std::string> value, const optional<std::string>& klass) { - if (value == getFillPattern(klass)) +void FillLayer::setFillPattern(PropertyValue<std::string> value) { + if (value == getFillPattern()) return; - impl->cascading.template get<FillPattern>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<FillPattern>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void FillLayer::setFillPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<FillPattern>().setTransition(value, klass); +void FillLayer::setFillPatternTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<FillPattern>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions FillLayer::getFillPatternTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<FillPattern>().getTransition(klass); +TransitionOptions FillLayer::getFillPatternTransition() const { + return impl().paint.template get<FillPattern>().options; } } // namespace style diff --git a/src/mbgl/style/layers/fill_layer_impl.cpp b/src/mbgl/style/layers/fill_layer_impl.cpp index 6ec55a58e3..0dc7ed14d5 100644 --- a/src/mbgl/style/layers/fill_layer_impl.cpp +++ b/src/mbgl/style/layers/fill_layer_impl.cpp @@ -1,11 +1,14 @@ #include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/renderer/render_fill_layer.hpp> namespace mbgl { namespace style { -std::unique_ptr<RenderLayer> FillLayer::Impl::createRenderLayer() const { - return std::make_unique<RenderFillLayer>(*this); +bool FillLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { + assert(dynamic_cast<const FillLayer::Impl*>(&other)); + const auto& impl = static_cast<const style::FillLayer::Impl&>(other); + return filter != impl.filter || + visibility != impl.visibility || + paint.hasDataDrivenPropertyDifference(impl.paint); } } // namespace style diff --git a/src/mbgl/style/layers/fill_layer_impl.hpp b/src/mbgl/style/layers/fill_layer_impl.hpp index 215558962e..2673cd7443 100644 --- a/src/mbgl/style/layers/fill_layer_impl.hpp +++ b/src/mbgl/style/layers/fill_layer_impl.hpp @@ -9,13 +9,12 @@ namespace style { class FillLayer::Impl : public Layer::Impl { public: - std::unique_ptr<Layer> clone() const override; - std::unique_ptr<Layer> cloneRef(const std::string& id) const override; - void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; + using Layer::Impl::Impl; - std::unique_ptr<RenderLayer> createRenderLayer() const override; + bool hasLayoutDifference(const Layer::Impl&) const override; + void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - FillPaintProperties::Cascading cascading; + FillPaintProperties::Transitionable paint; }; } // namespace style diff --git a/src/mbgl/style/layers/fill_layer_properties.hpp b/src/mbgl/style/layers/fill_layer_properties.hpp index ee1e5158ce..cb01194515 100644 --- a/src/mbgl/style/layers/fill_layer_properties.hpp +++ b/src/mbgl/style/layers/fill_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> @@ -39,7 +40,7 @@ struct FillPattern : CrossFadedPaintProperty<std::string> { static std::string defaultValue() { return ""; } }; -class FillPaintProperties : public PaintProperties< +class FillPaintProperties : public Properties< FillAntialias, FillOpacity, FillColor, diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index 2f690c3158..573aabda8b 100644 --- a/src/mbgl/style/layers/layer.cpp.ejs +++ b/src/mbgl/style/layers/layer.cpp.ejs @@ -7,47 +7,45 @@ #include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp> #include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer_impl.hpp> -#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { <% if (type === 'background') { -%> <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const std::string& layerID) - : Layer(LayerType::<%- camelize(type) %>, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; + : Layer(makeMutable<Impl>(LayerType::<%- camelize(type) %>, layerID, std::string())) { } <% } else { -%> <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const std::string& layerID, const std::string& sourceID) - : Layer(LayerType::<%- camelize(type) %>, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; - impl->source = sourceID; + : Layer(makeMutable<Impl>(LayerType::<%- camelize(type) %>, layerID, sourceID)) { } <% } -%> -<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const Impl& other) - : Layer(LayerType::<%- camelize(type) %>, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(Immutable<Impl> impl_) + : Layer(std::move(impl_)) { } <%- camelize(type) %>Layer::~<%- camelize(type) %>Layer() = default; -std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::clone() const { - return std::make_unique<<%- camelize(type) %>Layer>(*this); +const <%- camelize(type) %>Layer::Impl& <%- camelize(type) %>Layer::impl() const { + return static_cast<const Impl&>(*baseImpl); } -std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::cloneRef(const std::string& id_) const { - auto result = std::make_unique<<%- camelize(type) %>Layer>(*this); - result->impl->id = id_; - result->impl->cascading = <%- camelize(type) %>PaintProperties::Cascading(); - return std::move(result); +Mutable<<%- camelize(type) %>Layer::Impl> <%- camelize(type) %>Layer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> <%- camelize(type) %>Layer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = <%- camelize(type) %>PaintProperties::Transitionable(); + return std::make_unique<<%- camelize(type) %>Layer>(std::move(impl_)); } <% if (layoutProperties.length) { -%> void <%- camelize(type) %>Layer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const { - conversion::stringify(writer, layout); + layout.stringify(writer); } <% } else { -%> void <%- camelize(type) %>Layer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const { @@ -58,31 +56,60 @@ void <%- camelize(type) %>Layer::Impl::stringifyLayout(rapidjson::Writer<rapidjs // Source const std::string& <%- camelize(type) %>Layer::getSourceID() const { - return impl->source; + return impl().source; } <% if (type !== 'raster') { -%> void <%- camelize(type) %>Layer::setSourceLayer(const std::string& sourceLayer) { - impl->sourceLayer = sourceLayer; + auto impl_ = mutableImpl(); + impl_->sourceLayer = sourceLayer; + baseImpl = std::move(impl_); } const std::string& <%- camelize(type) %>Layer::getSourceLayer() const { - return impl->sourceLayer; + return impl().sourceLayer; } // Filter void <%- camelize(type) %>Layer::setFilter(const Filter& filter) { - impl->filter = filter; - impl->observer->onLayerFilterChanged(*this); + auto impl_ = mutableImpl(); + impl_->filter = filter; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } const Filter& <%- camelize(type) %>Layer::getFilter() const { - return impl->filter; + return impl().filter; } <% } -%> <% } -%> +// Visibility + +void <%- camelize(type) %>Layer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void <%- camelize(type) %>Layer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void <%- camelize(type) %>Layer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); +} + // Layout properties <% for (const property of layoutProperties) { -%> @@ -91,14 +118,16 @@ const Filter& <%- camelize(type) %>Layer::getFilter() const { } <%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const { - return impl->layout.unevaluated.get<<%- camelize(property.name) %>>(); + return impl().layout.get<<%- camelize(property.name) %>>(); } void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> value) { if (value == get<%- camelize(property.name) %>()) return; - impl->layout.unevaluated.get<<%- camelize(property.name) %>>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "<%- property.name %>"); + auto impl_ = mutableImpl(); + impl_->layout.get<<%- camelize(property.name) %>>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } <% } -%> @@ -108,31 +137,27 @@ void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyV return { <%- defaultValue(property) %> }; } -<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>(const optional<std::string>& klass) const { - return impl->cascading.template get<<%- camelize(property.name) %>>().get(klass); +<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const { + return impl().paint.template get<<%- camelize(property.name) %>>().value; } -void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> value, const optional<std::string>& klass) { - if (value == get<%- camelize(property.name) %>(klass)) +void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> value) { + if (value == get<%- camelize(property.name) %>()) return; - impl->cascading.template get<<%- camelize(property.name) %>>().set(value, klass); -<% if (isDataDriven(property)) { -%> - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } -<% } else { -%> - impl->observer->onLayerPaintPropertyChanged(*this); -<% } -%> + auto impl_ = mutableImpl(); + impl_->paint.template get<<%- camelize(property.name) %>>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>Transition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<<%- camelize(property.name) %>>().setTransition(value, klass); +void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>Transition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<<%- camelize(property.name) %>>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions <%- camelize(type) %>Layer::get<%- camelize(property.name) %>Transition(const optional<std::string>& klass) const { - return impl->cascading.template get<<%- camelize(property.name) %>>().getTransition(klass); +TransitionOptions <%- camelize(type) %>Layer::get<%- camelize(property.name) %>Transition() const { + return impl().paint.template get<<%- camelize(property.name) %>>().options; } <% } -%> diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs index 2a736ca388..cde1b80b7b 100644 --- a/src/mbgl/style/layers/layer_properties.hpp.ejs +++ b/src/mbgl/style/layers/layer_properties.hpp.ejs @@ -10,7 +10,9 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/programs/attributes.hpp> +#include <mbgl/programs/uniforms.hpp> namespace mbgl { namespace style { @@ -29,7 +31,7 @@ struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %> <% } -%> <% if (layoutProperties.length) { -%> -class <%- camelize(type) %>LayoutProperties : public LayoutProperties< +class <%- camelize(type) %>LayoutProperties : public Properties< <% for (const property of layoutProperties.slice(0, -1)) { -%> <%- camelize(property.name) %>, <% } -%> @@ -37,7 +39,7 @@ class <%- camelize(type) %>LayoutProperties : public LayoutProperties< > {}; <% } -%> -class <%- camelize(type) %>PaintProperties : public PaintProperties< +class <%- camelize(type) %>PaintProperties : public Properties< <% for (const property of paintProperties.slice(0, -1)) { -%> <%- camelize(property.name) %>, <% } -%> diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp index 7f1575aad5..1c7f0d28ee 100644 --- a/src/mbgl/style/layers/line_layer.cpp +++ b/src/mbgl/style/layers/line_layer.cpp @@ -2,63 +2,92 @@ #include <mbgl/style/layers/line_layer.hpp> #include <mbgl/style/layers/line_layer_impl.hpp> -#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { LineLayer::LineLayer(const std::string& layerID, const std::string& sourceID) - : Layer(LayerType::Line, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; - impl->source = sourceID; + : Layer(makeMutable<Impl>(LayerType::Line, layerID, sourceID)) { } -LineLayer::LineLayer(const Impl& other) - : Layer(LayerType::Line, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +LineLayer::LineLayer(Immutable<Impl> impl_) + : Layer(std::move(impl_)) { } LineLayer::~LineLayer() = default; -std::unique_ptr<Layer> LineLayer::Impl::clone() const { - return std::make_unique<LineLayer>(*this); +const LineLayer::Impl& LineLayer::impl() const { + return static_cast<const Impl&>(*baseImpl); } -std::unique_ptr<Layer> LineLayer::Impl::cloneRef(const std::string& id_) const { - auto result = std::make_unique<LineLayer>(*this); - result->impl->id = id_; - result->impl->cascading = LinePaintProperties::Cascading(); - return std::move(result); +Mutable<LineLayer::Impl> LineLayer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> LineLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = LinePaintProperties::Transitionable(); + return std::make_unique<LineLayer>(std::move(impl_)); } void LineLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const { - conversion::stringify(writer, layout); + layout.stringify(writer); } // Source const std::string& LineLayer::getSourceID() const { - return impl->source; + return impl().source; } void LineLayer::setSourceLayer(const std::string& sourceLayer) { - impl->sourceLayer = sourceLayer; + auto impl_ = mutableImpl(); + impl_->sourceLayer = sourceLayer; + baseImpl = std::move(impl_); } const std::string& LineLayer::getSourceLayer() const { - return impl->sourceLayer; + return impl().sourceLayer; } // Filter void LineLayer::setFilter(const Filter& filter) { - impl->filter = filter; - impl->observer->onLayerFilterChanged(*this); + auto impl_ = mutableImpl(); + impl_->filter = filter; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } const Filter& LineLayer::getFilter() const { - return impl->filter; + return impl().filter; +} + +// Visibility + +void LineLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void LineLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void LineLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); } // Layout properties @@ -68,56 +97,64 @@ PropertyValue<LineCapType> LineLayer::getDefaultLineCap() { } PropertyValue<LineCapType> LineLayer::getLineCap() const { - return impl->layout.unevaluated.get<LineCap>(); + return impl().layout.get<LineCap>(); } void LineLayer::setLineCap(PropertyValue<LineCapType> value) { if (value == getLineCap()) return; - impl->layout.unevaluated.get<LineCap>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "line-cap"); + auto impl_ = mutableImpl(); + impl_->layout.get<LineCap>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -PropertyValue<LineJoinType> LineLayer::getDefaultLineJoin() { +DataDrivenPropertyValue<LineJoinType> LineLayer::getDefaultLineJoin() { return LineJoin::defaultValue(); } -PropertyValue<LineJoinType> LineLayer::getLineJoin() const { - return impl->layout.unevaluated.get<LineJoin>(); +DataDrivenPropertyValue<LineJoinType> LineLayer::getLineJoin() const { + return impl().layout.get<LineJoin>(); } -void LineLayer::setLineJoin(PropertyValue<LineJoinType> value) { +void LineLayer::setLineJoin(DataDrivenPropertyValue<LineJoinType> value) { if (value == getLineJoin()) return; - impl->layout.unevaluated.get<LineJoin>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "line-join"); + auto impl_ = mutableImpl(); + impl_->layout.get<LineJoin>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<float> LineLayer::getDefaultLineMiterLimit() { return LineMiterLimit::defaultValue(); } PropertyValue<float> LineLayer::getLineMiterLimit() const { - return impl->layout.unevaluated.get<LineMiterLimit>(); + return impl().layout.get<LineMiterLimit>(); } void LineLayer::setLineMiterLimit(PropertyValue<float> value) { if (value == getLineMiterLimit()) return; - impl->layout.unevaluated.get<LineMiterLimit>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "line-miter-limit"); + auto impl_ = mutableImpl(); + impl_->layout.get<LineMiterLimit>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<float> LineLayer::getDefaultLineRoundLimit() { return LineRoundLimit::defaultValue(); } PropertyValue<float> LineLayer::getLineRoundLimit() const { - return impl->layout.unevaluated.get<LineRoundLimit>(); + return impl().layout.get<LineRoundLimit>(); } void LineLayer::setLineRoundLimit(PropertyValue<float> value) { if (value == getLineRoundLimit()) return; - impl->layout.unevaluated.get<LineRoundLimit>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "line-round-limit"); + auto impl_ = mutableImpl(); + impl_->layout.get<LineRoundLimit>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Paint properties @@ -126,250 +163,270 @@ DataDrivenPropertyValue<float> LineLayer::getDefaultLineOpacity() { return { 1 }; } -DataDrivenPropertyValue<float> LineLayer::getLineOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<LineOpacity>().get(klass); +DataDrivenPropertyValue<float> LineLayer::getLineOpacity() const { + return impl().paint.template get<LineOpacity>().value; } -void LineLayer::setLineOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getLineOpacity(klass)) +void LineLayer::setLineOpacity(DataDrivenPropertyValue<float> value) { + if (value == getLineOpacity()) return; - impl->cascading.template get<LineOpacity>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<LineOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineOpacity>().setTransition(value, klass); +void LineLayer::setLineOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineOpacity>().getTransition(klass); +TransitionOptions LineLayer::getLineOpacityTransition() const { + return impl().paint.template get<LineOpacity>().options; } DataDrivenPropertyValue<Color> LineLayer::getDefaultLineColor() { return { Color::black() }; } -DataDrivenPropertyValue<Color> LineLayer::getLineColor(const optional<std::string>& klass) const { - return impl->cascading.template get<LineColor>().get(klass); +DataDrivenPropertyValue<Color> LineLayer::getLineColor() const { + return impl().paint.template get<LineColor>().value; } -void LineLayer::setLineColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getLineColor(klass)) +void LineLayer::setLineColor(DataDrivenPropertyValue<Color> value) { + if (value == getLineColor()) return; - impl->cascading.template get<LineColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<LineColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineColor>().setTransition(value, klass); +void LineLayer::setLineColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineColor>().getTransition(klass); +TransitionOptions LineLayer::getLineColorTransition() const { + return impl().paint.template get<LineColor>().options; } PropertyValue<std::array<float, 2>> LineLayer::getDefaultLineTranslate() { return { {{ 0, 0 }} }; } -PropertyValue<std::array<float, 2>> LineLayer::getLineTranslate(const optional<std::string>& klass) const { - return impl->cascading.template get<LineTranslate>().get(klass); +PropertyValue<std::array<float, 2>> LineLayer::getLineTranslate() const { + return impl().paint.template get<LineTranslate>().value; } -void LineLayer::setLineTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) { - if (value == getLineTranslate(klass)) +void LineLayer::setLineTranslate(PropertyValue<std::array<float, 2>> value) { + if (value == getLineTranslate()) return; - impl->cascading.template get<LineTranslate>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<LineTranslate>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineTranslate>().setTransition(value, klass); +void LineLayer::setLineTranslateTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineTranslate>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineTranslateTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineTranslate>().getTransition(klass); +TransitionOptions LineLayer::getLineTranslateTransition() const { + return impl().paint.template get<LineTranslate>().options; } PropertyValue<TranslateAnchorType> LineLayer::getDefaultLineTranslateAnchor() { return { TranslateAnchorType::Map }; } -PropertyValue<TranslateAnchorType> LineLayer::getLineTranslateAnchor(const optional<std::string>& klass) const { - return impl->cascading.template get<LineTranslateAnchor>().get(klass); +PropertyValue<TranslateAnchorType> LineLayer::getLineTranslateAnchor() const { + return impl().paint.template get<LineTranslateAnchor>().value; } -void LineLayer::setLineTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) { - if (value == getLineTranslateAnchor(klass)) +void LineLayer::setLineTranslateAnchor(PropertyValue<TranslateAnchorType> value) { + if (value == getLineTranslateAnchor()) return; - impl->cascading.template get<LineTranslateAnchor>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<LineTranslateAnchor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineTranslateAnchor>().setTransition(value, klass); +void LineLayer::setLineTranslateAnchorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineTranslateAnchor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineTranslateAnchorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineTranslateAnchor>().getTransition(klass); +TransitionOptions LineLayer::getLineTranslateAnchorTransition() const { + return impl().paint.template get<LineTranslateAnchor>().options; } -PropertyValue<float> LineLayer::getDefaultLineWidth() { +DataDrivenPropertyValue<float> LineLayer::getDefaultLineWidth() { return { 1 }; } -PropertyValue<float> LineLayer::getLineWidth(const optional<std::string>& klass) const { - return impl->cascading.template get<LineWidth>().get(klass); +DataDrivenPropertyValue<float> LineLayer::getLineWidth() const { + return impl().paint.template get<LineWidth>().value; } -void LineLayer::setLineWidth(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getLineWidth(klass)) +void LineLayer::setLineWidth(DataDrivenPropertyValue<float> value) { + if (value == getLineWidth()) return; - impl->cascading.template get<LineWidth>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<LineWidth>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineWidth>().setTransition(value, klass); +void LineLayer::setLineWidthTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineWidth>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineWidthTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineWidth>().getTransition(klass); +TransitionOptions LineLayer::getLineWidthTransition() const { + return impl().paint.template get<LineWidth>().options; } DataDrivenPropertyValue<float> LineLayer::getDefaultLineGapWidth() { return { 0 }; } -DataDrivenPropertyValue<float> LineLayer::getLineGapWidth(const optional<std::string>& klass) const { - return impl->cascading.template get<LineGapWidth>().get(klass); +DataDrivenPropertyValue<float> LineLayer::getLineGapWidth() const { + return impl().paint.template get<LineGapWidth>().value; } -void LineLayer::setLineGapWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getLineGapWidth(klass)) +void LineLayer::setLineGapWidth(DataDrivenPropertyValue<float> value) { + if (value == getLineGapWidth()) return; - impl->cascading.template get<LineGapWidth>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<LineGapWidth>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineGapWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineGapWidth>().setTransition(value, klass); +void LineLayer::setLineGapWidthTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineGapWidth>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineGapWidthTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineGapWidth>().getTransition(klass); +TransitionOptions LineLayer::getLineGapWidthTransition() const { + return impl().paint.template get<LineGapWidth>().options; } DataDrivenPropertyValue<float> LineLayer::getDefaultLineOffset() { return { 0 }; } -DataDrivenPropertyValue<float> LineLayer::getLineOffset(const optional<std::string>& klass) const { - return impl->cascading.template get<LineOffset>().get(klass); +DataDrivenPropertyValue<float> LineLayer::getLineOffset() const { + return impl().paint.template get<LineOffset>().value; } -void LineLayer::setLineOffset(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getLineOffset(klass)) +void LineLayer::setLineOffset(DataDrivenPropertyValue<float> value) { + if (value == getLineOffset()) return; - impl->cascading.template get<LineOffset>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<LineOffset>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineOffsetTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineOffset>().setTransition(value, klass); +void LineLayer::setLineOffsetTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineOffset>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineOffsetTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineOffset>().getTransition(klass); +TransitionOptions LineLayer::getLineOffsetTransition() const { + return impl().paint.template get<LineOffset>().options; } DataDrivenPropertyValue<float> LineLayer::getDefaultLineBlur() { return { 0 }; } -DataDrivenPropertyValue<float> LineLayer::getLineBlur(const optional<std::string>& klass) const { - return impl->cascading.template get<LineBlur>().get(klass); +DataDrivenPropertyValue<float> LineLayer::getLineBlur() const { + return impl().paint.template get<LineBlur>().value; } -void LineLayer::setLineBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getLineBlur(klass)) +void LineLayer::setLineBlur(DataDrivenPropertyValue<float> value) { + if (value == getLineBlur()) return; - impl->cascading.template get<LineBlur>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<LineBlur>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineBlur>().setTransition(value, klass); +void LineLayer::setLineBlurTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineBlur>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineBlurTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineBlur>().getTransition(klass); +TransitionOptions LineLayer::getLineBlurTransition() const { + return impl().paint.template get<LineBlur>().options; } PropertyValue<std::vector<float>> LineLayer::getDefaultLineDasharray() { return { { } }; } -PropertyValue<std::vector<float>> LineLayer::getLineDasharray(const optional<std::string>& klass) const { - return impl->cascading.template get<LineDasharray>().get(klass); +PropertyValue<std::vector<float>> LineLayer::getLineDasharray() const { + return impl().paint.template get<LineDasharray>().value; } -void LineLayer::setLineDasharray(PropertyValue<std::vector<float>> value, const optional<std::string>& klass) { - if (value == getLineDasharray(klass)) +void LineLayer::setLineDasharray(PropertyValue<std::vector<float>> value) { + if (value == getLineDasharray()) return; - impl->cascading.template get<LineDasharray>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<LineDasharray>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLineDasharrayTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LineDasharray>().setTransition(value, klass); +void LineLayer::setLineDasharrayTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineDasharray>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLineDasharrayTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LineDasharray>().getTransition(klass); +TransitionOptions LineLayer::getLineDasharrayTransition() const { + return impl().paint.template get<LineDasharray>().options; } PropertyValue<std::string> LineLayer::getDefaultLinePattern() { return { "" }; } -PropertyValue<std::string> LineLayer::getLinePattern(const optional<std::string>& klass) const { - return impl->cascading.template get<LinePattern>().get(klass); +PropertyValue<std::string> LineLayer::getLinePattern() const { + return impl().paint.template get<LinePattern>().value; } -void LineLayer::setLinePattern(PropertyValue<std::string> value, const optional<std::string>& klass) { - if (value == getLinePattern(klass)) +void LineLayer::setLinePattern(PropertyValue<std::string> value) { + if (value == getLinePattern()) return; - impl->cascading.template get<LinePattern>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<LinePattern>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void LineLayer::setLinePatternTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<LinePattern>().setTransition(value, klass); +void LineLayer::setLinePatternTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LinePattern>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions LineLayer::getLinePatternTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<LinePattern>().getTransition(klass); +TransitionOptions LineLayer::getLinePatternTransition() const { + return impl().paint.template get<LinePattern>().options; } } // namespace style diff --git a/src/mbgl/style/layers/line_layer_impl.cpp b/src/mbgl/style/layers/line_layer_impl.cpp index 973a77abf4..bee88d6a47 100644 --- a/src/mbgl/style/layers/line_layer_impl.cpp +++ b/src/mbgl/style/layers/line_layer_impl.cpp @@ -1,11 +1,15 @@ #include <mbgl/style/layers/line_layer_impl.hpp> -#include <mbgl/renderer/render_line_layer.hpp> namespace mbgl { namespace style { -std::unique_ptr<RenderLayer> LineLayer::Impl::createRenderLayer() const { - return std::make_unique<RenderLineLayer>(*this); +bool LineLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { + assert(dynamic_cast<const LineLayer::Impl*>(&other)); + const auto& impl = static_cast<const style::LineLayer::Impl&>(other); + return filter != impl.filter || + visibility != impl.visibility || + layout != impl.layout || + paint.hasDataDrivenPropertyDifference(impl.paint); } } // namespace style diff --git a/src/mbgl/style/layers/line_layer_impl.hpp b/src/mbgl/style/layers/line_layer_impl.hpp index 02c9c85f00..04adc0e85c 100644 --- a/src/mbgl/style/layers/line_layer_impl.hpp +++ b/src/mbgl/style/layers/line_layer_impl.hpp @@ -9,14 +9,13 @@ namespace style { class LineLayer::Impl : public Layer::Impl { public: - std::unique_ptr<Layer> clone() const override; - std::unique_ptr<Layer> cloneRef(const std::string& id) const override; - void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; + using Layer::Impl::Impl; - std::unique_ptr<RenderLayer> createRenderLayer() const override; + bool hasLayoutDifference(const Layer::Impl&) const override; + void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - LineLayoutProperties layout; - LinePaintProperties::Cascading cascading; + LineLayoutProperties::Unevaluated layout; + LinePaintProperties::Transitionable paint; }; } // namespace style diff --git a/src/mbgl/style/layers/line_layer_properties.hpp b/src/mbgl/style/layers/line_layer_properties.hpp index 6c301e6a0e..aeaf51698a 100644 --- a/src/mbgl/style/layers/line_layer_properties.hpp +++ b/src/mbgl/style/layers/line_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> @@ -16,7 +17,7 @@ struct LineCap : LayoutProperty<LineCapType> { static LineCapType defaultValue() { return LineCapType::Butt; } }; -struct LineJoin : LayoutProperty<LineJoinType> { +struct LineJoin : DataDrivenLayoutProperty<LineJoinType> { static constexpr const char * key = "line-join"; static LineJoinType defaultValue() { return LineJoinType::Miter; } }; @@ -47,7 +48,7 @@ struct LineTranslateAnchor : PaintProperty<TranslateAnchorType> { static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; } }; -struct LineWidth : PaintProperty<float> { +struct LineWidth : DataDrivenPaintProperty<float, attributes::a_width, uniforms::u_width> { static float defaultValue() { return 1; } }; @@ -71,14 +72,14 @@ struct LinePattern : CrossFadedPaintProperty<std::string> { static std::string defaultValue() { return ""; } }; -class LineLayoutProperties : public LayoutProperties< +class LineLayoutProperties : public Properties< LineCap, LineJoin, LineMiterLimit, LineRoundLimit > {}; -class LinePaintProperties : public PaintProperties< +class LinePaintProperties : public Properties< LineOpacity, LineColor, LineTranslate, diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp index b525f9eaa4..a9a8d273fa 100644 --- a/src/mbgl/style/layers/raster_layer.cpp +++ b/src/mbgl/style/layers/raster_layer.cpp @@ -2,34 +2,34 @@ #include <mbgl/style/layers/raster_layer.hpp> #include <mbgl/style/layers/raster_layer_impl.hpp> -#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { RasterLayer::RasterLayer(const std::string& layerID, const std::string& sourceID) - : Layer(LayerType::Raster, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; - impl->source = sourceID; + : Layer(makeMutable<Impl>(LayerType::Raster, layerID, sourceID)) { } -RasterLayer::RasterLayer(const Impl& other) - : Layer(LayerType::Raster, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +RasterLayer::RasterLayer(Immutable<Impl> impl_) + : Layer(std::move(impl_)) { } RasterLayer::~RasterLayer() = default; -std::unique_ptr<Layer> RasterLayer::Impl::clone() const { - return std::make_unique<RasterLayer>(*this); +const RasterLayer::Impl& RasterLayer::impl() const { + return static_cast<const Impl&>(*baseImpl); } -std::unique_ptr<Layer> RasterLayer::Impl::cloneRef(const std::string& id_) const { - auto result = std::make_unique<RasterLayer>(*this); - result->impl->id = id_; - result->impl->cascading = RasterPaintProperties::Cascading(); - return std::move(result); +Mutable<RasterLayer::Impl> RasterLayer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> RasterLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = RasterPaintProperties::Transitionable(); + return std::make_unique<RasterLayer>(std::move(impl_)); } void RasterLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const { @@ -38,10 +38,35 @@ void RasterLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffe // Source const std::string& RasterLayer::getSourceID() const { - return impl->source; + return impl().source; } +// Visibility + +void RasterLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void RasterLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void RasterLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); +} + // Layout properties @@ -51,161 +76,189 @@ PropertyValue<float> RasterLayer::getDefaultRasterOpacity() { return { 1 }; } -PropertyValue<float> RasterLayer::getRasterOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterOpacity>().get(klass); +PropertyValue<float> RasterLayer::getRasterOpacity() const { + return impl().paint.template get<RasterOpacity>().value; } -void RasterLayer::setRasterOpacity(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getRasterOpacity(klass)) +void RasterLayer::setRasterOpacity(PropertyValue<float> value) { + if (value == getRasterOpacity()) return; - impl->cascading.template get<RasterOpacity>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void RasterLayer::setRasterOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<RasterOpacity>().setTransition(value, klass); +void RasterLayer::setRasterOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions RasterLayer::getRasterOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterOpacity>().getTransition(klass); +TransitionOptions RasterLayer::getRasterOpacityTransition() const { + return impl().paint.template get<RasterOpacity>().options; } PropertyValue<float> RasterLayer::getDefaultRasterHueRotate() { return { 0 }; } -PropertyValue<float> RasterLayer::getRasterHueRotate(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterHueRotate>().get(klass); +PropertyValue<float> RasterLayer::getRasterHueRotate() const { + return impl().paint.template get<RasterHueRotate>().value; } -void RasterLayer::setRasterHueRotate(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getRasterHueRotate(klass)) +void RasterLayer::setRasterHueRotate(PropertyValue<float> value) { + if (value == getRasterHueRotate()) return; - impl->cascading.template get<RasterHueRotate>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterHueRotate>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void RasterLayer::setRasterHueRotateTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<RasterHueRotate>().setTransition(value, klass); +void RasterLayer::setRasterHueRotateTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterHueRotate>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions RasterLayer::getRasterHueRotateTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterHueRotate>().getTransition(klass); +TransitionOptions RasterLayer::getRasterHueRotateTransition() const { + return impl().paint.template get<RasterHueRotate>().options; } PropertyValue<float> RasterLayer::getDefaultRasterBrightnessMin() { return { 0 }; } -PropertyValue<float> RasterLayer::getRasterBrightnessMin(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterBrightnessMin>().get(klass); +PropertyValue<float> RasterLayer::getRasterBrightnessMin() const { + return impl().paint.template get<RasterBrightnessMin>().value; } -void RasterLayer::setRasterBrightnessMin(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getRasterBrightnessMin(klass)) +void RasterLayer::setRasterBrightnessMin(PropertyValue<float> value) { + if (value == getRasterBrightnessMin()) return; - impl->cascading.template get<RasterBrightnessMin>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterBrightnessMin>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void RasterLayer::setRasterBrightnessMinTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<RasterBrightnessMin>().setTransition(value, klass); +void RasterLayer::setRasterBrightnessMinTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterBrightnessMin>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions RasterLayer::getRasterBrightnessMinTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterBrightnessMin>().getTransition(klass); +TransitionOptions RasterLayer::getRasterBrightnessMinTransition() const { + return impl().paint.template get<RasterBrightnessMin>().options; } PropertyValue<float> RasterLayer::getDefaultRasterBrightnessMax() { return { 1 }; } -PropertyValue<float> RasterLayer::getRasterBrightnessMax(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterBrightnessMax>().get(klass); +PropertyValue<float> RasterLayer::getRasterBrightnessMax() const { + return impl().paint.template get<RasterBrightnessMax>().value; } -void RasterLayer::setRasterBrightnessMax(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getRasterBrightnessMax(klass)) +void RasterLayer::setRasterBrightnessMax(PropertyValue<float> value) { + if (value == getRasterBrightnessMax()) return; - impl->cascading.template get<RasterBrightnessMax>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterBrightnessMax>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void RasterLayer::setRasterBrightnessMaxTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<RasterBrightnessMax>().setTransition(value, klass); +void RasterLayer::setRasterBrightnessMaxTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterBrightnessMax>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions RasterLayer::getRasterBrightnessMaxTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterBrightnessMax>().getTransition(klass); +TransitionOptions RasterLayer::getRasterBrightnessMaxTransition() const { + return impl().paint.template get<RasterBrightnessMax>().options; } PropertyValue<float> RasterLayer::getDefaultRasterSaturation() { return { 0 }; } -PropertyValue<float> RasterLayer::getRasterSaturation(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterSaturation>().get(klass); +PropertyValue<float> RasterLayer::getRasterSaturation() const { + return impl().paint.template get<RasterSaturation>().value; } -void RasterLayer::setRasterSaturation(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getRasterSaturation(klass)) +void RasterLayer::setRasterSaturation(PropertyValue<float> value) { + if (value == getRasterSaturation()) return; - impl->cascading.template get<RasterSaturation>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterSaturation>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void RasterLayer::setRasterSaturationTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<RasterSaturation>().setTransition(value, klass); +void RasterLayer::setRasterSaturationTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterSaturation>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions RasterLayer::getRasterSaturationTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterSaturation>().getTransition(klass); +TransitionOptions RasterLayer::getRasterSaturationTransition() const { + return impl().paint.template get<RasterSaturation>().options; } PropertyValue<float> RasterLayer::getDefaultRasterContrast() { return { 0 }; } -PropertyValue<float> RasterLayer::getRasterContrast(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterContrast>().get(klass); +PropertyValue<float> RasterLayer::getRasterContrast() const { + return impl().paint.template get<RasterContrast>().value; } -void RasterLayer::setRasterContrast(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getRasterContrast(klass)) +void RasterLayer::setRasterContrast(PropertyValue<float> value) { + if (value == getRasterContrast()) return; - impl->cascading.template get<RasterContrast>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterContrast>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void RasterLayer::setRasterContrastTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<RasterContrast>().setTransition(value, klass); +void RasterLayer::setRasterContrastTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterContrast>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions RasterLayer::getRasterContrastTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterContrast>().getTransition(klass); +TransitionOptions RasterLayer::getRasterContrastTransition() const { + return impl().paint.template get<RasterContrast>().options; } PropertyValue<float> RasterLayer::getDefaultRasterFadeDuration() { return { 300 }; } -PropertyValue<float> RasterLayer::getRasterFadeDuration(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterFadeDuration>().get(klass); +PropertyValue<float> RasterLayer::getRasterFadeDuration() const { + return impl().paint.template get<RasterFadeDuration>().value; } -void RasterLayer::setRasterFadeDuration(PropertyValue<float> value, const optional<std::string>& klass) { - if (value == getRasterFadeDuration(klass)) +void RasterLayer::setRasterFadeDuration(PropertyValue<float> value) { + if (value == getRasterFadeDuration()) return; - impl->cascading.template get<RasterFadeDuration>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterFadeDuration>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void RasterLayer::setRasterFadeDurationTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<RasterFadeDuration>().setTransition(value, klass); +void RasterLayer::setRasterFadeDurationTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<RasterFadeDuration>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions RasterLayer::getRasterFadeDurationTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<RasterFadeDuration>().getTransition(klass); +TransitionOptions RasterLayer::getRasterFadeDurationTransition() const { + return impl().paint.template get<RasterFadeDuration>().options; } } // namespace style diff --git a/src/mbgl/style/layers/raster_layer_impl.cpp b/src/mbgl/style/layers/raster_layer_impl.cpp index fa9f80dac6..a995f50dd3 100644 --- a/src/mbgl/style/layers/raster_layer_impl.cpp +++ b/src/mbgl/style/layers/raster_layer_impl.cpp @@ -1,11 +1,10 @@ #include <mbgl/style/layers/raster_layer_impl.hpp> -#include <mbgl/renderer/render_raster_layer.hpp> namespace mbgl { namespace style { -std::unique_ptr<RenderLayer> RasterLayer::Impl::createRenderLayer() const { - return std::make_unique<RenderRasterLayer>(*this); +bool RasterLayer::Impl::hasLayoutDifference(const Layer::Impl&) const { + return false; } } // namespace style diff --git a/src/mbgl/style/layers/raster_layer_impl.hpp b/src/mbgl/style/layers/raster_layer_impl.hpp index edf5f9111b..adbe703e92 100644 --- a/src/mbgl/style/layers/raster_layer_impl.hpp +++ b/src/mbgl/style/layers/raster_layer_impl.hpp @@ -9,13 +9,12 @@ namespace style { class RasterLayer::Impl : public Layer::Impl { public: - std::unique_ptr<Layer> clone() const override; - std::unique_ptr<Layer> cloneRef(const std::string& id) const override; - void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; + using Layer::Impl::Impl; - std::unique_ptr<RenderLayer> createRenderLayer() const override; + bool hasLayoutDifference(const Layer::Impl&) const override; + void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - RasterPaintProperties::Cascading cascading; + RasterPaintProperties::Transitionable paint; }; } // namespace style diff --git a/src/mbgl/style/layers/raster_layer_properties.hpp b/src/mbgl/style/layers/raster_layer_properties.hpp index 219fe34d8c..12df09f32c 100644 --- a/src/mbgl/style/layers/raster_layer_properties.hpp +++ b/src/mbgl/style/layers/raster_layer_properties.hpp @@ -5,7 +5,9 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/programs/attributes.hpp> +#include <mbgl/programs/uniforms.hpp> namespace mbgl { namespace style { @@ -38,7 +40,7 @@ struct RasterFadeDuration : PaintProperty<float> { static float defaultValue() { return 300; } }; -class RasterPaintProperties : public PaintProperties< +class RasterPaintProperties : public Properties< RasterOpacity, RasterHueRotate, RasterBrightnessMin, diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index 273a9fd24e..9a944657ca 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -2,63 +2,92 @@ #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/style/layer_observer.hpp> namespace mbgl { namespace style { SymbolLayer::SymbolLayer(const std::string& layerID, const std::string& sourceID) - : Layer(LayerType::Symbol, std::make_unique<Impl>()) - , impl(static_cast<Impl*>(baseImpl.get())) { - impl->id = layerID; - impl->source = sourceID; + : Layer(makeMutable<Impl>(LayerType::Symbol, layerID, sourceID)) { } -SymbolLayer::SymbolLayer(const Impl& other) - : Layer(LayerType::Symbol, std::make_unique<Impl>(other)) - , impl(static_cast<Impl*>(baseImpl.get())) { +SymbolLayer::SymbolLayer(Immutable<Impl> impl_) + : Layer(std::move(impl_)) { } SymbolLayer::~SymbolLayer() = default; -std::unique_ptr<Layer> SymbolLayer::Impl::clone() const { - return std::make_unique<SymbolLayer>(*this); +const SymbolLayer::Impl& SymbolLayer::impl() const { + return static_cast<const Impl&>(*baseImpl); } -std::unique_ptr<Layer> SymbolLayer::Impl::cloneRef(const std::string& id_) const { - auto result = std::make_unique<SymbolLayer>(*this); - result->impl->id = id_; - result->impl->cascading = SymbolPaintProperties::Cascading(); - return std::move(result); +Mutable<SymbolLayer::Impl> SymbolLayer::mutableImpl() const { + return makeMutable<Impl>(impl()); +} + +std::unique_ptr<Layer> SymbolLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = SymbolPaintProperties::Transitionable(); + return std::make_unique<SymbolLayer>(std::move(impl_)); } void SymbolLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const { - conversion::stringify(writer, layout); + layout.stringify(writer); } // Source const std::string& SymbolLayer::getSourceID() const { - return impl->source; + return impl().source; } void SymbolLayer::setSourceLayer(const std::string& sourceLayer) { - impl->sourceLayer = sourceLayer; + auto impl_ = mutableImpl(); + impl_->sourceLayer = sourceLayer; + baseImpl = std::move(impl_); } const std::string& SymbolLayer::getSourceLayer() const { - return impl->sourceLayer; + return impl().sourceLayer; } // Filter void SymbolLayer::setFilter(const Filter& filter) { - impl->filter = filter; - impl->observer->onLayerFilterChanged(*this); + auto impl_ = mutableImpl(); + impl_->filter = filter; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } const Filter& SymbolLayer::getFilter() const { - return impl->filter; + return impl().filter; +} + +// Visibility + +void SymbolLayer::setVisibility(VisibilityType value) { + if (value == getVisibility()) + return; + auto impl_ = mutableImpl(); + impl_->visibility = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Zoom range + +void SymbolLayer::setMinZoom(float minZoom) { + auto impl_ = mutableImpl(); + impl_->minZoom = minZoom; + baseImpl = std::move(impl_); +} + +void SymbolLayer::setMaxZoom(float maxZoom) { + auto impl_ = mutableImpl(); + impl_->maxZoom = maxZoom; + baseImpl = std::move(impl_); } // Layout properties @@ -68,476 +97,576 @@ PropertyValue<SymbolPlacementType> SymbolLayer::getDefaultSymbolPlacement() { } PropertyValue<SymbolPlacementType> SymbolLayer::getSymbolPlacement() const { - return impl->layout.unevaluated.get<SymbolPlacement>(); + return impl().layout.get<SymbolPlacement>(); } void SymbolLayer::setSymbolPlacement(PropertyValue<SymbolPlacementType> value) { if (value == getSymbolPlacement()) return; - impl->layout.unevaluated.get<SymbolPlacement>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "symbol-placement"); + auto impl_ = mutableImpl(); + impl_->layout.get<SymbolPlacement>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<float> SymbolLayer::getDefaultSymbolSpacing() { return SymbolSpacing::defaultValue(); } PropertyValue<float> SymbolLayer::getSymbolSpacing() const { - return impl->layout.unevaluated.get<SymbolSpacing>(); + return impl().layout.get<SymbolSpacing>(); } void SymbolLayer::setSymbolSpacing(PropertyValue<float> value) { if (value == getSymbolSpacing()) return; - impl->layout.unevaluated.get<SymbolSpacing>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "symbol-spacing"); + auto impl_ = mutableImpl(); + impl_->layout.get<SymbolSpacing>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultSymbolAvoidEdges() { return SymbolAvoidEdges::defaultValue(); } PropertyValue<bool> SymbolLayer::getSymbolAvoidEdges() const { - return impl->layout.unevaluated.get<SymbolAvoidEdges>(); + return impl().layout.get<SymbolAvoidEdges>(); } void SymbolLayer::setSymbolAvoidEdges(PropertyValue<bool> value) { if (value == getSymbolAvoidEdges()) return; - impl->layout.unevaluated.get<SymbolAvoidEdges>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "symbol-avoid-edges"); + auto impl_ = mutableImpl(); + impl_->layout.get<SymbolAvoidEdges>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultIconAllowOverlap() { return IconAllowOverlap::defaultValue(); } PropertyValue<bool> SymbolLayer::getIconAllowOverlap() const { - return impl->layout.unevaluated.get<IconAllowOverlap>(); + return impl().layout.get<IconAllowOverlap>(); } void SymbolLayer::setIconAllowOverlap(PropertyValue<bool> value) { if (value == getIconAllowOverlap()) return; - impl->layout.unevaluated.get<IconAllowOverlap>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-allow-overlap"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconAllowOverlap>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultIconIgnorePlacement() { return IconIgnorePlacement::defaultValue(); } PropertyValue<bool> SymbolLayer::getIconIgnorePlacement() const { - return impl->layout.unevaluated.get<IconIgnorePlacement>(); + return impl().layout.get<IconIgnorePlacement>(); } void SymbolLayer::setIconIgnorePlacement(PropertyValue<bool> value) { if (value == getIconIgnorePlacement()) return; - impl->layout.unevaluated.get<IconIgnorePlacement>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-ignore-placement"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconIgnorePlacement>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultIconOptional() { return IconOptional::defaultValue(); } PropertyValue<bool> SymbolLayer::getIconOptional() const { - return impl->layout.unevaluated.get<IconOptional>(); + return impl().layout.get<IconOptional>(); } void SymbolLayer::setIconOptional(PropertyValue<bool> value) { if (value == getIconOptional()) return; - impl->layout.unevaluated.get<IconOptional>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-optional"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconOptional>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<AlignmentType> SymbolLayer::getDefaultIconRotationAlignment() { return IconRotationAlignment::defaultValue(); } PropertyValue<AlignmentType> SymbolLayer::getIconRotationAlignment() const { - return impl->layout.unevaluated.get<IconRotationAlignment>(); + return impl().layout.get<IconRotationAlignment>(); } void SymbolLayer::setIconRotationAlignment(PropertyValue<AlignmentType> value) { if (value == getIconRotationAlignment()) return; - impl->layout.unevaluated.get<IconRotationAlignment>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-rotation-alignment"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconRotationAlignment>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconSize() { return IconSize::defaultValue(); } DataDrivenPropertyValue<float> SymbolLayer::getIconSize() const { - return impl->layout.unevaluated.get<IconSize>(); + return impl().layout.get<IconSize>(); } void SymbolLayer::setIconSize(DataDrivenPropertyValue<float> value) { if (value == getIconSize()) return; - impl->layout.unevaluated.get<IconSize>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-size"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconSize>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<IconTextFitType> SymbolLayer::getDefaultIconTextFit() { return IconTextFit::defaultValue(); } PropertyValue<IconTextFitType> SymbolLayer::getIconTextFit() const { - return impl->layout.unevaluated.get<IconTextFit>(); + return impl().layout.get<IconTextFit>(); } void SymbolLayer::setIconTextFit(PropertyValue<IconTextFitType> value) { if (value == getIconTextFit()) return; - impl->layout.unevaluated.get<IconTextFit>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-text-fit"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconTextFit>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<std::array<float, 4>> SymbolLayer::getDefaultIconTextFitPadding() { return IconTextFitPadding::defaultValue(); } PropertyValue<std::array<float, 4>> SymbolLayer::getIconTextFitPadding() const { - return impl->layout.unevaluated.get<IconTextFitPadding>(); + return impl().layout.get<IconTextFitPadding>(); } void SymbolLayer::setIconTextFitPadding(PropertyValue<std::array<float, 4>> value) { if (value == getIconTextFitPadding()) return; - impl->layout.unevaluated.get<IconTextFitPadding>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-text-fit-padding"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconTextFitPadding>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<std::string> SymbolLayer::getDefaultIconImage() { return IconImage::defaultValue(); } DataDrivenPropertyValue<std::string> SymbolLayer::getIconImage() const { - return impl->layout.unevaluated.get<IconImage>(); + return impl().layout.get<IconImage>(); } void SymbolLayer::setIconImage(DataDrivenPropertyValue<std::string> value) { if (value == getIconImage()) return; - impl->layout.unevaluated.get<IconImage>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-image"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconImage>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconRotate() { return IconRotate::defaultValue(); } DataDrivenPropertyValue<float> SymbolLayer::getIconRotate() const { - return impl->layout.unevaluated.get<IconRotate>(); + return impl().layout.get<IconRotate>(); } void SymbolLayer::setIconRotate(DataDrivenPropertyValue<float> value) { if (value == getIconRotate()) return; - impl->layout.unevaluated.get<IconRotate>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-rotate"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconRotate>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<float> SymbolLayer::getDefaultIconPadding() { return IconPadding::defaultValue(); } PropertyValue<float> SymbolLayer::getIconPadding() const { - return impl->layout.unevaluated.get<IconPadding>(); + return impl().layout.get<IconPadding>(); } void SymbolLayer::setIconPadding(PropertyValue<float> value) { if (value == getIconPadding()) return; - impl->layout.unevaluated.get<IconPadding>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-padding"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconPadding>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultIconKeepUpright() { return IconKeepUpright::defaultValue(); } PropertyValue<bool> SymbolLayer::getIconKeepUpright() const { - return impl->layout.unevaluated.get<IconKeepUpright>(); + return impl().layout.get<IconKeepUpright>(); } void SymbolLayer::setIconKeepUpright(PropertyValue<bool> value) { if (value == getIconKeepUpright()) return; - impl->layout.unevaluated.get<IconKeepUpright>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-keep-upright"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconKeepUpright>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getDefaultIconOffset() { return IconOffset::defaultValue(); } DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getIconOffset() const { - return impl->layout.unevaluated.get<IconOffset>(); + return impl().layout.get<IconOffset>(); } void SymbolLayer::setIconOffset(DataDrivenPropertyValue<std::array<float, 2>> value) { if (value == getIconOffset()) return; - impl->layout.unevaluated.get<IconOffset>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "icon-offset"); + auto impl_ = mutableImpl(); + impl_->layout.get<IconOffset>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultIconAnchor() { + return IconAnchor::defaultValue(); +} + +DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getIconAnchor() const { + return impl().layout.get<IconAnchor>(); +} + +void SymbolLayer::setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) { + if (value == getIconAnchor()) + return; + auto impl_ = mutableImpl(); + impl_->layout.get<IconAnchor>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue<AlignmentType> SymbolLayer::getDefaultIconPitchAlignment() { + return IconPitchAlignment::defaultValue(); +} + +PropertyValue<AlignmentType> SymbolLayer::getIconPitchAlignment() const { + return impl().layout.get<IconPitchAlignment>(); +} + +void SymbolLayer::setIconPitchAlignment(PropertyValue<AlignmentType> value) { + if (value == getIconPitchAlignment()) + return; + auto impl_ = mutableImpl(); + impl_->layout.get<IconPitchAlignment>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<AlignmentType> SymbolLayer::getDefaultTextPitchAlignment() { return TextPitchAlignment::defaultValue(); } PropertyValue<AlignmentType> SymbolLayer::getTextPitchAlignment() const { - return impl->layout.unevaluated.get<TextPitchAlignment>(); + return impl().layout.get<TextPitchAlignment>(); } void SymbolLayer::setTextPitchAlignment(PropertyValue<AlignmentType> value) { if (value == getTextPitchAlignment()) return; - impl->layout.unevaluated.get<TextPitchAlignment>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-pitch-alignment"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextPitchAlignment>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<AlignmentType> SymbolLayer::getDefaultTextRotationAlignment() { return TextRotationAlignment::defaultValue(); } PropertyValue<AlignmentType> SymbolLayer::getTextRotationAlignment() const { - return impl->layout.unevaluated.get<TextRotationAlignment>(); + return impl().layout.get<TextRotationAlignment>(); } void SymbolLayer::setTextRotationAlignment(PropertyValue<AlignmentType> value) { if (value == getTextRotationAlignment()) return; - impl->layout.unevaluated.get<TextRotationAlignment>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-rotation-alignment"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextRotationAlignment>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<std::string> SymbolLayer::getDefaultTextField() { return TextField::defaultValue(); } DataDrivenPropertyValue<std::string> SymbolLayer::getTextField() const { - return impl->layout.unevaluated.get<TextField>(); + return impl().layout.get<TextField>(); } void SymbolLayer::setTextField(DataDrivenPropertyValue<std::string> value) { if (value == getTextField()) return; - impl->layout.unevaluated.get<TextField>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-field"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextField>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<std::vector<std::string>> SymbolLayer::getDefaultTextFont() { return TextFont::defaultValue(); } PropertyValue<std::vector<std::string>> SymbolLayer::getTextFont() const { - return impl->layout.unevaluated.get<TextFont>(); + return impl().layout.get<TextFont>(); } void SymbolLayer::setTextFont(PropertyValue<std::vector<std::string>> value) { if (value == getTextFont()) return; - impl->layout.unevaluated.get<TextFont>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-font"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextFont>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextSize() { return TextSize::defaultValue(); } DataDrivenPropertyValue<float> SymbolLayer::getTextSize() const { - return impl->layout.unevaluated.get<TextSize>(); + return impl().layout.get<TextSize>(); } void SymbolLayer::setTextSize(DataDrivenPropertyValue<float> value) { if (value == getTextSize()) return; - impl->layout.unevaluated.get<TextSize>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-size"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextSize>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -PropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() { return TextMaxWidth::defaultValue(); } -PropertyValue<float> SymbolLayer::getTextMaxWidth() const { - return impl->layout.unevaluated.get<TextMaxWidth>(); +DataDrivenPropertyValue<float> SymbolLayer::getTextMaxWidth() const { + return impl().layout.get<TextMaxWidth>(); } -void SymbolLayer::setTextMaxWidth(PropertyValue<float> value) { +void SymbolLayer::setTextMaxWidth(DataDrivenPropertyValue<float> value) { if (value == getTextMaxWidth()) return; - impl->layout.unevaluated.get<TextMaxWidth>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-max-width"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextMaxWidth>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<float> SymbolLayer::getDefaultTextLineHeight() { return TextLineHeight::defaultValue(); } PropertyValue<float> SymbolLayer::getTextLineHeight() const { - return impl->layout.unevaluated.get<TextLineHeight>(); + return impl().layout.get<TextLineHeight>(); } void SymbolLayer::setTextLineHeight(PropertyValue<float> value) { if (value == getTextLineHeight()) return; - impl->layout.unevaluated.get<TextLineHeight>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-line-height"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextLineHeight>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -PropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() { return TextLetterSpacing::defaultValue(); } -PropertyValue<float> SymbolLayer::getTextLetterSpacing() const { - return impl->layout.unevaluated.get<TextLetterSpacing>(); +DataDrivenPropertyValue<float> SymbolLayer::getTextLetterSpacing() const { + return impl().layout.get<TextLetterSpacing>(); } -void SymbolLayer::setTextLetterSpacing(PropertyValue<float> value) { +void SymbolLayer::setTextLetterSpacing(DataDrivenPropertyValue<float> value) { if (value == getTextLetterSpacing()) return; - impl->layout.unevaluated.get<TextLetterSpacing>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-letter-spacing"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextLetterSpacing>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -PropertyValue<TextJustifyType> SymbolLayer::getDefaultTextJustify() { +DataDrivenPropertyValue<TextJustifyType> SymbolLayer::getDefaultTextJustify() { return TextJustify::defaultValue(); } -PropertyValue<TextJustifyType> SymbolLayer::getTextJustify() const { - return impl->layout.unevaluated.get<TextJustify>(); +DataDrivenPropertyValue<TextJustifyType> SymbolLayer::getTextJustify() const { + return impl().layout.get<TextJustify>(); } -void SymbolLayer::setTextJustify(PropertyValue<TextJustifyType> value) { +void SymbolLayer::setTextJustify(DataDrivenPropertyValue<TextJustifyType> value) { if (value == getTextJustify()) return; - impl->layout.unevaluated.get<TextJustify>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-justify"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextJustify>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -PropertyValue<TextAnchorType> SymbolLayer::getDefaultTextAnchor() { +DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultTextAnchor() { return TextAnchor::defaultValue(); } -PropertyValue<TextAnchorType> SymbolLayer::getTextAnchor() const { - return impl->layout.unevaluated.get<TextAnchor>(); +DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getTextAnchor() const { + return impl().layout.get<TextAnchor>(); } -void SymbolLayer::setTextAnchor(PropertyValue<TextAnchorType> value) { +void SymbolLayer::setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) { if (value == getTextAnchor()) return; - impl->layout.unevaluated.get<TextAnchor>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-anchor"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextAnchor>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<float> SymbolLayer::getDefaultTextMaxAngle() { return TextMaxAngle::defaultValue(); } PropertyValue<float> SymbolLayer::getTextMaxAngle() const { - return impl->layout.unevaluated.get<TextMaxAngle>(); + return impl().layout.get<TextMaxAngle>(); } void SymbolLayer::setTextMaxAngle(PropertyValue<float> value) { if (value == getTextMaxAngle()) return; - impl->layout.unevaluated.get<TextMaxAngle>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-max-angle"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextMaxAngle>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextRotate() { return TextRotate::defaultValue(); } DataDrivenPropertyValue<float> SymbolLayer::getTextRotate() const { - return impl->layout.unevaluated.get<TextRotate>(); + return impl().layout.get<TextRotate>(); } void SymbolLayer::setTextRotate(DataDrivenPropertyValue<float> value) { if (value == getTextRotate()) return; - impl->layout.unevaluated.get<TextRotate>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-rotate"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextRotate>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<float> SymbolLayer::getDefaultTextPadding() { return TextPadding::defaultValue(); } PropertyValue<float> SymbolLayer::getTextPadding() const { - return impl->layout.unevaluated.get<TextPadding>(); + return impl().layout.get<TextPadding>(); } void SymbolLayer::setTextPadding(PropertyValue<float> value) { if (value == getTextPadding()) return; - impl->layout.unevaluated.get<TextPadding>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-padding"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextPadding>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultTextKeepUpright() { return TextKeepUpright::defaultValue(); } PropertyValue<bool> SymbolLayer::getTextKeepUpright() const { - return impl->layout.unevaluated.get<TextKeepUpright>(); + return impl().layout.get<TextKeepUpright>(); } void SymbolLayer::setTextKeepUpright(PropertyValue<bool> value) { if (value == getTextKeepUpright()) return; - impl->layout.unevaluated.get<TextKeepUpright>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-keep-upright"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextKeepUpright>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<TextTransformType> SymbolLayer::getDefaultTextTransform() { return TextTransform::defaultValue(); } DataDrivenPropertyValue<TextTransformType> SymbolLayer::getTextTransform() const { - return impl->layout.unevaluated.get<TextTransform>(); + return impl().layout.get<TextTransform>(); } void SymbolLayer::setTextTransform(DataDrivenPropertyValue<TextTransformType> value) { if (value == getTextTransform()) return; - impl->layout.unevaluated.get<TextTransform>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-transform"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextTransform>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getDefaultTextOffset() { return TextOffset::defaultValue(); } DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getTextOffset() const { - return impl->layout.unevaluated.get<TextOffset>(); + return impl().layout.get<TextOffset>(); } void SymbolLayer::setTextOffset(DataDrivenPropertyValue<std::array<float, 2>> value) { if (value == getTextOffset()) return; - impl->layout.unevaluated.get<TextOffset>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-offset"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextOffset>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultTextAllowOverlap() { return TextAllowOverlap::defaultValue(); } PropertyValue<bool> SymbolLayer::getTextAllowOverlap() const { - return impl->layout.unevaluated.get<TextAllowOverlap>(); + return impl().layout.get<TextAllowOverlap>(); } void SymbolLayer::setTextAllowOverlap(PropertyValue<bool> value) { if (value == getTextAllowOverlap()) return; - impl->layout.unevaluated.get<TextAllowOverlap>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-allow-overlap"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextAllowOverlap>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultTextIgnorePlacement() { return TextIgnorePlacement::defaultValue(); } PropertyValue<bool> SymbolLayer::getTextIgnorePlacement() const { - return impl->layout.unevaluated.get<TextIgnorePlacement>(); + return impl().layout.get<TextIgnorePlacement>(); } void SymbolLayer::setTextIgnorePlacement(PropertyValue<bool> value) { if (value == getTextIgnorePlacement()) return; - impl->layout.unevaluated.get<TextIgnorePlacement>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-ignore-placement"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextIgnorePlacement>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } PropertyValue<bool> SymbolLayer::getDefaultTextOptional() { return TextOptional::defaultValue(); } PropertyValue<bool> SymbolLayer::getTextOptional() const { - return impl->layout.unevaluated.get<TextOptional>(); + return impl().layout.get<TextOptional>(); } void SymbolLayer::setTextOptional(PropertyValue<bool> value) { if (value == getTextOptional()) return; - impl->layout.unevaluated.get<TextOptional>() = value; - impl->observer->onLayerLayoutPropertyChanged(*this, "text-optional"); + auto impl_ = mutableImpl(); + impl_->layout.get<TextOptional>() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } // Paint properties @@ -546,362 +675,378 @@ DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconOpacity() { return { 1 }; } -DataDrivenPropertyValue<float> SymbolLayer::getIconOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<IconOpacity>().get(klass); +DataDrivenPropertyValue<float> SymbolLayer::getIconOpacity() const { + return impl().paint.template get<IconOpacity>().value; } -void SymbolLayer::setIconOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getIconOpacity(klass)) +void SymbolLayer::setIconOpacity(DataDrivenPropertyValue<float> value) { + if (value == getIconOpacity()) return; - impl->cascading.template get<IconOpacity>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<IconOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setIconOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<IconOpacity>().setTransition(value, klass); +void SymbolLayer::setIconOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<IconOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getIconOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<IconOpacity>().getTransition(klass); +TransitionOptions SymbolLayer::getIconOpacityTransition() const { + return impl().paint.template get<IconOpacity>().options; } DataDrivenPropertyValue<Color> SymbolLayer::getDefaultIconColor() { return { Color::black() }; } -DataDrivenPropertyValue<Color> SymbolLayer::getIconColor(const optional<std::string>& klass) const { - return impl->cascading.template get<IconColor>().get(klass); +DataDrivenPropertyValue<Color> SymbolLayer::getIconColor() const { + return impl().paint.template get<IconColor>().value; } -void SymbolLayer::setIconColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getIconColor(klass)) +void SymbolLayer::setIconColor(DataDrivenPropertyValue<Color> value) { + if (value == getIconColor()) return; - impl->cascading.template get<IconColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<IconColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setIconColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<IconColor>().setTransition(value, klass); +void SymbolLayer::setIconColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<IconColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getIconColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<IconColor>().getTransition(klass); +TransitionOptions SymbolLayer::getIconColorTransition() const { + return impl().paint.template get<IconColor>().options; } DataDrivenPropertyValue<Color> SymbolLayer::getDefaultIconHaloColor() { return { {} }; } -DataDrivenPropertyValue<Color> SymbolLayer::getIconHaloColor(const optional<std::string>& klass) const { - return impl->cascading.template get<IconHaloColor>().get(klass); +DataDrivenPropertyValue<Color> SymbolLayer::getIconHaloColor() const { + return impl().paint.template get<IconHaloColor>().value; } -void SymbolLayer::setIconHaloColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getIconHaloColor(klass)) +void SymbolLayer::setIconHaloColor(DataDrivenPropertyValue<Color> value) { + if (value == getIconHaloColor()) return; - impl->cascading.template get<IconHaloColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<IconHaloColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setIconHaloColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<IconHaloColor>().setTransition(value, klass); +void SymbolLayer::setIconHaloColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<IconHaloColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getIconHaloColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<IconHaloColor>().getTransition(klass); +TransitionOptions SymbolLayer::getIconHaloColorTransition() const { + return impl().paint.template get<IconHaloColor>().options; } DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconHaloWidth() { return { 0 }; } -DataDrivenPropertyValue<float> SymbolLayer::getIconHaloWidth(const optional<std::string>& klass) const { - return impl->cascading.template get<IconHaloWidth>().get(klass); +DataDrivenPropertyValue<float> SymbolLayer::getIconHaloWidth() const { + return impl().paint.template get<IconHaloWidth>().value; } -void SymbolLayer::setIconHaloWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getIconHaloWidth(klass)) +void SymbolLayer::setIconHaloWidth(DataDrivenPropertyValue<float> value) { + if (value == getIconHaloWidth()) return; - impl->cascading.template get<IconHaloWidth>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<IconHaloWidth>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setIconHaloWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<IconHaloWidth>().setTransition(value, klass); +void SymbolLayer::setIconHaloWidthTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<IconHaloWidth>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getIconHaloWidthTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<IconHaloWidth>().getTransition(klass); +TransitionOptions SymbolLayer::getIconHaloWidthTransition() const { + return impl().paint.template get<IconHaloWidth>().options; } DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconHaloBlur() { return { 0 }; } -DataDrivenPropertyValue<float> SymbolLayer::getIconHaloBlur(const optional<std::string>& klass) const { - return impl->cascading.template get<IconHaloBlur>().get(klass); +DataDrivenPropertyValue<float> SymbolLayer::getIconHaloBlur() const { + return impl().paint.template get<IconHaloBlur>().value; } -void SymbolLayer::setIconHaloBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getIconHaloBlur(klass)) +void SymbolLayer::setIconHaloBlur(DataDrivenPropertyValue<float> value) { + if (value == getIconHaloBlur()) return; - impl->cascading.template get<IconHaloBlur>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<IconHaloBlur>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setIconHaloBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<IconHaloBlur>().setTransition(value, klass); +void SymbolLayer::setIconHaloBlurTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<IconHaloBlur>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getIconHaloBlurTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<IconHaloBlur>().getTransition(klass); +TransitionOptions SymbolLayer::getIconHaloBlurTransition() const { + return impl().paint.template get<IconHaloBlur>().options; } PropertyValue<std::array<float, 2>> SymbolLayer::getDefaultIconTranslate() { return { {{ 0, 0 }} }; } -PropertyValue<std::array<float, 2>> SymbolLayer::getIconTranslate(const optional<std::string>& klass) const { - return impl->cascading.template get<IconTranslate>().get(klass); +PropertyValue<std::array<float, 2>> SymbolLayer::getIconTranslate() const { + return impl().paint.template get<IconTranslate>().value; } -void SymbolLayer::setIconTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) { - if (value == getIconTranslate(klass)) +void SymbolLayer::setIconTranslate(PropertyValue<std::array<float, 2>> value) { + if (value == getIconTranslate()) return; - impl->cascading.template get<IconTranslate>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<IconTranslate>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setIconTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<IconTranslate>().setTransition(value, klass); +void SymbolLayer::setIconTranslateTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<IconTranslate>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getIconTranslateTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<IconTranslate>().getTransition(klass); +TransitionOptions SymbolLayer::getIconTranslateTransition() const { + return impl().paint.template get<IconTranslate>().options; } PropertyValue<TranslateAnchorType> SymbolLayer::getDefaultIconTranslateAnchor() { return { TranslateAnchorType::Map }; } -PropertyValue<TranslateAnchorType> SymbolLayer::getIconTranslateAnchor(const optional<std::string>& klass) const { - return impl->cascading.template get<IconTranslateAnchor>().get(klass); +PropertyValue<TranslateAnchorType> SymbolLayer::getIconTranslateAnchor() const { + return impl().paint.template get<IconTranslateAnchor>().value; } -void SymbolLayer::setIconTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) { - if (value == getIconTranslateAnchor(klass)) +void SymbolLayer::setIconTranslateAnchor(PropertyValue<TranslateAnchorType> value) { + if (value == getIconTranslateAnchor()) return; - impl->cascading.template get<IconTranslateAnchor>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<IconTranslateAnchor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setIconTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<IconTranslateAnchor>().setTransition(value, klass); +void SymbolLayer::setIconTranslateAnchorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<IconTranslateAnchor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getIconTranslateAnchorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<IconTranslateAnchor>().getTransition(klass); +TransitionOptions SymbolLayer::getIconTranslateAnchorTransition() const { + return impl().paint.template get<IconTranslateAnchor>().options; } DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextOpacity() { return { 1 }; } -DataDrivenPropertyValue<float> SymbolLayer::getTextOpacity(const optional<std::string>& klass) const { - return impl->cascading.template get<TextOpacity>().get(klass); +DataDrivenPropertyValue<float> SymbolLayer::getTextOpacity() const { + return impl().paint.template get<TextOpacity>().value; } -void SymbolLayer::setTextOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getTextOpacity(klass)) +void SymbolLayer::setTextOpacity(DataDrivenPropertyValue<float> value) { + if (value == getTextOpacity()) return; - impl->cascading.template get<TextOpacity>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<TextOpacity>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setTextOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<TextOpacity>().setTransition(value, klass); +void SymbolLayer::setTextOpacityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<TextOpacity>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getTextOpacityTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<TextOpacity>().getTransition(klass); +TransitionOptions SymbolLayer::getTextOpacityTransition() const { + return impl().paint.template get<TextOpacity>().options; } DataDrivenPropertyValue<Color> SymbolLayer::getDefaultTextColor() { return { Color::black() }; } -DataDrivenPropertyValue<Color> SymbolLayer::getTextColor(const optional<std::string>& klass) const { - return impl->cascading.template get<TextColor>().get(klass); +DataDrivenPropertyValue<Color> SymbolLayer::getTextColor() const { + return impl().paint.template get<TextColor>().value; } -void SymbolLayer::setTextColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getTextColor(klass)) +void SymbolLayer::setTextColor(DataDrivenPropertyValue<Color> value) { + if (value == getTextColor()) return; - impl->cascading.template get<TextColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<TextColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setTextColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<TextColor>().setTransition(value, klass); +void SymbolLayer::setTextColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<TextColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getTextColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<TextColor>().getTransition(klass); +TransitionOptions SymbolLayer::getTextColorTransition() const { + return impl().paint.template get<TextColor>().options; } DataDrivenPropertyValue<Color> SymbolLayer::getDefaultTextHaloColor() { return { {} }; } -DataDrivenPropertyValue<Color> SymbolLayer::getTextHaloColor(const optional<std::string>& klass) const { - return impl->cascading.template get<TextHaloColor>().get(klass); +DataDrivenPropertyValue<Color> SymbolLayer::getTextHaloColor() const { + return impl().paint.template get<TextHaloColor>().value; } -void SymbolLayer::setTextHaloColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { - if (value == getTextHaloColor(klass)) +void SymbolLayer::setTextHaloColor(DataDrivenPropertyValue<Color> value) { + if (value == getTextHaloColor()) return; - impl->cascading.template get<TextHaloColor>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<TextHaloColor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setTextHaloColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<TextHaloColor>().setTransition(value, klass); +void SymbolLayer::setTextHaloColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<TextHaloColor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getTextHaloColorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<TextHaloColor>().getTransition(klass); +TransitionOptions SymbolLayer::getTextHaloColorTransition() const { + return impl().paint.template get<TextHaloColor>().options; } DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextHaloWidth() { return { 0 }; } -DataDrivenPropertyValue<float> SymbolLayer::getTextHaloWidth(const optional<std::string>& klass) const { - return impl->cascading.template get<TextHaloWidth>().get(klass); +DataDrivenPropertyValue<float> SymbolLayer::getTextHaloWidth() const { + return impl().paint.template get<TextHaloWidth>().value; } -void SymbolLayer::setTextHaloWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getTextHaloWidth(klass)) +void SymbolLayer::setTextHaloWidth(DataDrivenPropertyValue<float> value) { + if (value == getTextHaloWidth()) return; - impl->cascading.template get<TextHaloWidth>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<TextHaloWidth>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setTextHaloWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<TextHaloWidth>().setTransition(value, klass); +void SymbolLayer::setTextHaloWidthTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<TextHaloWidth>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getTextHaloWidthTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<TextHaloWidth>().getTransition(klass); +TransitionOptions SymbolLayer::getTextHaloWidthTransition() const { + return impl().paint.template get<TextHaloWidth>().options; } DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextHaloBlur() { return { 0 }; } -DataDrivenPropertyValue<float> SymbolLayer::getTextHaloBlur(const optional<std::string>& klass) const { - return impl->cascading.template get<TextHaloBlur>().get(klass); +DataDrivenPropertyValue<float> SymbolLayer::getTextHaloBlur() const { + return impl().paint.template get<TextHaloBlur>().value; } -void SymbolLayer::setTextHaloBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { - if (value == getTextHaloBlur(klass)) +void SymbolLayer::setTextHaloBlur(DataDrivenPropertyValue<float> value) { + if (value == getTextHaloBlur()) return; - impl->cascading.template get<TextHaloBlur>().set(value, klass); - if (value.isDataDriven()) { - impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); - } else { - impl->observer->onLayerPaintPropertyChanged(*this); - } + auto impl_ = mutableImpl(); + impl_->paint.template get<TextHaloBlur>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setTextHaloBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<TextHaloBlur>().setTransition(value, klass); +void SymbolLayer::setTextHaloBlurTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<TextHaloBlur>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getTextHaloBlurTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<TextHaloBlur>().getTransition(klass); +TransitionOptions SymbolLayer::getTextHaloBlurTransition() const { + return impl().paint.template get<TextHaloBlur>().options; } PropertyValue<std::array<float, 2>> SymbolLayer::getDefaultTextTranslate() { return { {{ 0, 0 }} }; } -PropertyValue<std::array<float, 2>> SymbolLayer::getTextTranslate(const optional<std::string>& klass) const { - return impl->cascading.template get<TextTranslate>().get(klass); +PropertyValue<std::array<float, 2>> SymbolLayer::getTextTranslate() const { + return impl().paint.template get<TextTranslate>().value; } -void SymbolLayer::setTextTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) { - if (value == getTextTranslate(klass)) +void SymbolLayer::setTextTranslate(PropertyValue<std::array<float, 2>> value) { + if (value == getTextTranslate()) return; - impl->cascading.template get<TextTranslate>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<TextTranslate>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setTextTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<TextTranslate>().setTransition(value, klass); +void SymbolLayer::setTextTranslateTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<TextTranslate>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getTextTranslateTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<TextTranslate>().getTransition(klass); +TransitionOptions SymbolLayer::getTextTranslateTransition() const { + return impl().paint.template get<TextTranslate>().options; } PropertyValue<TranslateAnchorType> SymbolLayer::getDefaultTextTranslateAnchor() { return { TranslateAnchorType::Map }; } -PropertyValue<TranslateAnchorType> SymbolLayer::getTextTranslateAnchor(const optional<std::string>& klass) const { - return impl->cascading.template get<TextTranslateAnchor>().get(klass); +PropertyValue<TranslateAnchorType> SymbolLayer::getTextTranslateAnchor() const { + return impl().paint.template get<TextTranslateAnchor>().value; } -void SymbolLayer::setTextTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) { - if (value == getTextTranslateAnchor(klass)) +void SymbolLayer::setTextTranslateAnchor(PropertyValue<TranslateAnchorType> value) { + if (value == getTextTranslateAnchor()) return; - impl->cascading.template get<TextTranslateAnchor>().set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + auto impl_ = mutableImpl(); + impl_->paint.template get<TextTranslateAnchor>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); } -void SymbolLayer::setTextTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { - impl->cascading.template get<TextTranslateAnchor>().setTransition(value, klass); +void SymbolLayer::setTextTranslateAnchorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<TextTranslateAnchor>().options = options; + baseImpl = std::move(impl_); } -TransitionOptions SymbolLayer::getTextTranslateAnchorTransition(const optional<std::string>& klass) const { - return impl->cascading.template get<TextTranslateAnchor>().getTransition(klass); +TransitionOptions SymbolLayer::getTextTranslateAnchorTransition() const { + return impl().paint.template get<TextTranslateAnchor>().options; } } // namespace style diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index c99dd8ad70..b59768725d 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.cpp +++ b/src/mbgl/style/layers/symbol_layer_impl.cpp @@ -1,11 +1,15 @@ #include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> namespace mbgl { namespace style { -std::unique_ptr<RenderLayer> SymbolLayer::Impl::createRenderLayer() const { - return std::make_unique<RenderSymbolLayer>(*this); +bool SymbolLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { + assert(dynamic_cast<const SymbolLayer::Impl*>(&other)); + const auto& impl = static_cast<const style::SymbolLayer::Impl&>(other); + return filter != impl.filter || + visibility != impl.visibility || + layout != impl.layout || + paint.hasDataDrivenPropertyDifference(impl.paint); } } // namespace style diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp index df145647a0..f8ef87dcdf 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.hpp +++ b/src/mbgl/style/layers/symbol_layer_impl.hpp @@ -1,24 +1,21 @@ #pragma once -#include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/style/layer_impl.hpp> #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> namespace mbgl { - namespace style { class SymbolLayer::Impl : public Layer::Impl { public: - std::unique_ptr<Layer> clone() const override; - std::unique_ptr<Layer> cloneRef(const std::string& id) const override; - void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; + using Layer::Impl::Impl; - std::unique_ptr<RenderLayer> createRenderLayer() const override; + bool hasLayoutDifference(const Layer::Impl&) const override; + void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override; - SymbolLayoutProperties layout; - SymbolPaintProperties::Cascading cascading; + SymbolLayoutProperties::Unevaluated layout; + SymbolPaintProperties::Transitionable paint; }; } // namespace style diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index 5b57175785..436b5cbd4f 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> @@ -86,6 +87,16 @@ struct IconOffset : DataDrivenLayoutProperty<std::array<float, 2>> { static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; } }; +struct IconAnchor : DataDrivenLayoutProperty<SymbolAnchorType> { + static constexpr const char * key = "icon-anchor"; + static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; } +}; + +struct IconPitchAlignment : LayoutProperty<AlignmentType> { + static constexpr const char * key = "icon-pitch-alignment"; + static AlignmentType defaultValue() { return AlignmentType::Auto; } +}; + struct TextPitchAlignment : LayoutProperty<AlignmentType> { static constexpr const char * key = "text-pitch-alignment"; static AlignmentType defaultValue() { return AlignmentType::Auto; } @@ -111,7 +122,7 @@ struct TextSize : DataDrivenLayoutProperty<float> { static float defaultValue() { return 16; } }; -struct TextMaxWidth : LayoutProperty<float> { +struct TextMaxWidth : DataDrivenLayoutProperty<float> { static constexpr const char * key = "text-max-width"; static float defaultValue() { return 10; } }; @@ -121,19 +132,19 @@ struct TextLineHeight : LayoutProperty<float> { static float defaultValue() { return 1.2; } }; -struct TextLetterSpacing : LayoutProperty<float> { +struct TextLetterSpacing : DataDrivenLayoutProperty<float> { static constexpr const char * key = "text-letter-spacing"; static float defaultValue() { return 0; } }; -struct TextJustify : LayoutProperty<TextJustifyType> { +struct TextJustify : DataDrivenLayoutProperty<TextJustifyType> { static constexpr const char * key = "text-justify"; static TextJustifyType defaultValue() { return TextJustifyType::Center; } }; -struct TextAnchor : LayoutProperty<TextAnchorType> { +struct TextAnchor : DataDrivenLayoutProperty<SymbolAnchorType> { static constexpr const char * key = "text-anchor"; - static TextAnchorType defaultValue() { return TextAnchorType::Center; } + static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; } }; struct TextMaxAngle : LayoutProperty<float> { @@ -237,7 +248,7 @@ struct TextTranslateAnchor : PaintProperty<TranslateAnchorType> { static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; } }; -class SymbolLayoutProperties : public LayoutProperties< +class SymbolLayoutProperties : public Properties< SymbolPlacement, SymbolSpacing, SymbolAvoidEdges, @@ -253,6 +264,8 @@ class SymbolLayoutProperties : public LayoutProperties< IconPadding, IconKeepUpright, IconOffset, + IconAnchor, + IconPitchAlignment, TextPitchAlignment, TextRotationAlignment, TextField, @@ -274,7 +287,7 @@ class SymbolLayoutProperties : public LayoutProperties< TextOptional > {}; -class SymbolPaintProperties : public PaintProperties< +class SymbolPaintProperties : public Properties< IconOpacity, IconColor, IconHaloColor, diff --git a/src/mbgl/style/layout_property.hpp b/src/mbgl/style/layout_property.hpp index 3b9d6114c0..8c59295ad2 100644 --- a/src/mbgl/style/layout_property.hpp +++ b/src/mbgl/style/layout_property.hpp @@ -4,117 +4,30 @@ #include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/renderer/property_evaluator.hpp> #include <mbgl/renderer/data_driven_property_evaluator.hpp> -#include <mbgl/util/indexed_tuple.hpp> namespace mbgl { - -class PropertyEvaluationParameters; - namespace style { template <class T> class LayoutProperty { public: + using TransitionableType = std::nullptr_t; using UnevaluatedType = PropertyValue<T>; using EvaluatorType = PropertyEvaluator<T>; using PossiblyEvaluatedType = T; using Type = T; + static constexpr bool IsDataDriven = false; }; template <class T> class DataDrivenLayoutProperty { public: + using TransitionableType = std::nullptr_t; using UnevaluatedType = DataDrivenPropertyValue<T>; using EvaluatorType = DataDrivenPropertyEvaluator<T>; using PossiblyEvaluatedType = PossiblyEvaluatedPropertyValue<T>; using Type = T; -}; - -template <class... Ps> -class LayoutProperties { -public: - using Properties = TypeList<Ps...>; - - template <class TypeList> - using Tuple = IndexedTuple<Properties, TypeList>; - - /* - For layout properties we implement a two step evaluation process: if you have a zoom level, - you can evaluate a set of unevaluated property values, producing a set of possibly evaluated - values, where undefined, constant, or camera function values have been fully evaluated, and - source or composite function values have not. - - Once you also have a particular feature, you can evaluate that set of possibly evaluated values - fully, producing a set of fully evaluated values. - - This is in theory maximally efficient in terms of avoiding repeated evaluation of camera - functions, though it's more of a historical accident than a purposeful optimization. - */ - - using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>; - using PossiblyEvaluatedTypes = TypeList<typename Ps::PossiblyEvaluatedType...>; - using EvaluatedTypes = TypeList<typename Ps::Type...>; - - class Evaluated : public Tuple<EvaluatedTypes> { - public: - using Tuple<EvaluatedTypes>::Tuple; - }; - - class PossiblyEvaluated : public Tuple<PossiblyEvaluatedTypes> { - public: - using Tuple<PossiblyEvaluatedTypes>::Tuple; - - template <class T> - static T evaluate(float, const GeometryTileFeature&, const T& t, const T&) { - return t; - } - - template <class T> - static T evaluate(float z, const GeometryTileFeature& feature, - const PossiblyEvaluatedPropertyValue<T>& v, const T& defaultValue) { - return v.match( - [&] (const T& t) { - return t; - }, - [&] (const SourceFunction<T>& t) { - return t.evaluate(feature, defaultValue); - }, - [&] (const CompositeFunction<T>& t) { - return t.evaluate(z, feature, defaultValue); - }); - } - - template <class P> - auto evaluate(float z, const GeometryTileFeature& feature) const { - return evaluate(z, feature, this->template get<P>(), P::defaultValue()); - } - - Evaluated evaluate(float z, const GeometryTileFeature& feature) const { - return Evaluated { - evaluate<Ps>(z, feature)... - }; - } - }; - - class Unevaluated : public Tuple<UnevaluatedTypes> { - public: - using Tuple<UnevaluatedTypes>::Tuple; - }; - - template <class P> - auto evaluate(const PropertyEvaluationParameters& parameters) const { - using Evaluator = typename P::EvaluatorType; - return unevaluated.template get<P>() - .evaluate(Evaluator(parameters, P::defaultValue())); - } - - PossiblyEvaluated evaluate(const PropertyEvaluationParameters& parameters) const { - return PossiblyEvaluated { - evaluate<Ps>(parameters)... - }; - } - - Unevaluated unevaluated; + static constexpr bool IsDataDriven = true; }; } // namespace style diff --git a/src/mbgl/style/light.cpp b/src/mbgl/style/light.cpp index b54920713c..352dc4d942 100644 --- a/src/mbgl/style/light.cpp +++ b/src/mbgl/style/light.cpp @@ -2,17 +2,28 @@ #include <mbgl/style/light.hpp> #include <mbgl/style/light_impl.hpp> -#include <mbgl/style/light_properties.hpp> +#include <mbgl/style/light_observer.hpp> namespace mbgl { namespace style { +static LightObserver nullObserver; + Light::Light() - : impl(std::make_unique<Impl>()) { + : impl(makeMutable<Impl>()), + observer(&nullObserver) { } Light::~Light() = default; +void Light::setObserver(LightObserver* observer_) { + observer = observer_ ? observer_ : &nullObserver; +} + +Mutable<Light::Impl> Light::mutableImpl() const { + return makeMutable<Impl>(*impl); +} + LightAnchorType Light::getDefaultAnchor() { return LightAnchor::defaultValue(); } @@ -22,17 +33,21 @@ PropertyValue<LightAnchorType> Light::getAnchor() const { } void Light::setAnchor(PropertyValue<LightAnchorType> property) { - impl->properties.template get<LightAnchor>().value = property; - impl->observer->onLightChanged(*this); + auto impl_ = mutableImpl(); + impl_->properties.template get<LightAnchor>().value = property; + impl = std::move(impl_); + observer->onLightChanged(*this); } -void Light::setAnchorTransition(const TransitionOptions& transition) { - impl->properties.template get<LightAnchor>().transition = transition; - impl->observer->onLightChanged(*this); +void Light::setAnchorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->properties.template get<LightAnchor>().options = options; + impl = std::move(impl_); + observer->onLightChanged(*this); } TransitionOptions Light::getAnchorTransition() const { - return impl->properties.template get<LightAnchor>().transition; + return impl->properties.template get<LightAnchor>().options; } Position Light::getDefaultPosition() { @@ -44,17 +59,21 @@ PropertyValue<Position> Light::getPosition() const { } void Light::setPosition(PropertyValue<Position> property) { - impl->properties.template get<LightPosition>().value = property; - impl->observer->onLightChanged(*this); + auto impl_ = mutableImpl(); + impl_->properties.template get<LightPosition>().value = property; + impl = std::move(impl_); + observer->onLightChanged(*this); } -void Light::setPositionTransition(const TransitionOptions& transition) { - impl->properties.template get<LightPosition>().transition = transition; - impl->observer->onLightChanged(*this); +void Light::setPositionTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->properties.template get<LightPosition>().options = options; + impl = std::move(impl_); + observer->onLightChanged(*this); } TransitionOptions Light::getPositionTransition() const { - return impl->properties.template get<LightPosition>().transition; + return impl->properties.template get<LightPosition>().options; } Color Light::getDefaultColor() { @@ -66,17 +85,21 @@ PropertyValue<Color> Light::getColor() const { } void Light::setColor(PropertyValue<Color> property) { - impl->properties.template get<LightColor>().value = property; - impl->observer->onLightChanged(*this); + auto impl_ = mutableImpl(); + impl_->properties.template get<LightColor>().value = property; + impl = std::move(impl_); + observer->onLightChanged(*this); } -void Light::setColorTransition(const TransitionOptions& transition) { - impl->properties.template get<LightColor>().transition = transition; - impl->observer->onLightChanged(*this); +void Light::setColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->properties.template get<LightColor>().options = options; + impl = std::move(impl_); + observer->onLightChanged(*this); } TransitionOptions Light::getColorTransition() const { - return impl->properties.template get<LightColor>().transition; + return impl->properties.template get<LightColor>().options; } float Light::getDefaultIntensity() { @@ -88,17 +111,21 @@ PropertyValue<float> Light::getIntensity() const { } void Light::setIntensity(PropertyValue<float> property) { - impl->properties.template get<LightIntensity>().value = property; - impl->observer->onLightChanged(*this); + auto impl_ = mutableImpl(); + impl_->properties.template get<LightIntensity>().value = property; + impl = std::move(impl_); + observer->onLightChanged(*this); } -void Light::setIntensityTransition(const TransitionOptions& transition) { - impl->properties.template get<LightIntensity>().transition = transition; - impl->observer->onLightChanged(*this); +void Light::setIntensityTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->properties.template get<LightIntensity>().options = options; + impl = std::move(impl_); + observer->onLightChanged(*this); } TransitionOptions Light::getIntensityTransition() const { - return impl->properties.template get<LightIntensity>().transition; + return impl->properties.template get<LightIntensity>().options; } diff --git a/src/mbgl/style/light.cpp.ejs b/src/mbgl/style/light.cpp.ejs index c82c65c10c..45241c60fd 100644 --- a/src/mbgl/style/light.cpp.ejs +++ b/src/mbgl/style/light.cpp.ejs @@ -5,17 +5,28 @@ #include <mbgl/style/light.hpp> #include <mbgl/style/light_impl.hpp> -#include <mbgl/style/light_properties.hpp> +#include <mbgl/style/light_observer.hpp> namespace mbgl { namespace style { +static LightObserver nullObserver; + Light::Light() - : impl(std::make_unique<Impl>()) { + : impl(makeMutable<Impl>()), + observer(&nullObserver) { } Light::~Light() = default; +void Light::setObserver(LightObserver* observer_) { + observer = observer_ ? observer_ : &nullObserver; +} + +Mutable<Light::Impl> Light::mutableImpl() const { + return makeMutable<Impl>(*impl); +} + <% for (const property of properties) { -%> <%- evaluatedType(property) %> Light::getDefault<%- camelize(property.name) %>() { return Light<%- camelize(property.name) %>::defaultValue(); @@ -26,17 +37,21 @@ Light::~Light() = default; } void Light::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> property) { - impl->properties.template get<Light<%- camelize(property.name) %>>().value = property; - impl->observer->onLightChanged(*this); + auto impl_ = mutableImpl(); + impl_->properties.template get<Light<%- camelize(property.name) %>>().value = property; + impl = std::move(impl_); + observer->onLightChanged(*this); } -void Light::set<%- camelize(property.name) %>Transition(const TransitionOptions& transition) { - impl->properties.template get<Light<%- camelize(property.name) %>>().transition = transition; - impl->observer->onLightChanged(*this); +void Light::set<%- camelize(property.name) %>Transition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->properties.template get<Light<%- camelize(property.name) %>>().options = options; + impl = std::move(impl_); + observer->onLightChanged(*this); } TransitionOptions Light::get<%- camelize(property.name) %>Transition() const { - return impl->properties.template get<Light<%- camelize(property.name) %>>().transition; + return impl->properties.template get<Light<%- camelize(property.name) %>>().options; } <% } -%> diff --git a/src/mbgl/style/light_impl.cpp b/src/mbgl/style/light_impl.cpp index e0ab1176ed..619d115f02 100644 --- a/src/mbgl/style/light_impl.cpp +++ b/src/mbgl/style/light_impl.cpp @@ -3,9 +3,5 @@ namespace mbgl { namespace style { -void Light::Impl::setObserver(LightObserver* observer_) { - observer = observer_; -} - } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/light_impl.hpp b/src/mbgl/style/light_impl.hpp index b4fd886742..f094c9d462 100644 --- a/src/mbgl/style/light_impl.hpp +++ b/src/mbgl/style/light_impl.hpp @@ -1,19 +1,58 @@ #pragma once -#include <mbgl/style/light_properties.hpp> -#include <mbgl/style/light_observer.hpp> +#include <mbgl/style/light.hpp> +#include <mbgl/style/property_value.hpp> +#include <mbgl/style/types.hpp> +#include <mbgl/style/position.hpp> +#include <mbgl/style/properties.hpp> +#include <mbgl/renderer/property_evaluator.hpp> +#include <mbgl/util/color.hpp> +#include <mbgl/util/indexed_tuple.hpp> namespace mbgl { namespace style { -class Light::Impl { +template <class T> +class LightProperty { public: + using TransitionableType = Transitionable<PropertyValue<T>>; + using UnevaluatedType = Transitioning<PropertyValue<T>>; + using EvaluatorType = PropertyEvaluator<T>; + using PossiblyEvaluatedType = T; + using Type = T; + static constexpr bool IsDataDriven = false; +}; + +struct LightAnchor : LightProperty<LightAnchorType> { + static LightAnchorType defaultValue() { + return LightAnchorType::Viewport; + } +}; + +struct LightPosition : LightProperty<Position> { + static Position defaultValue() { + std::array<float, 3> default_ = { { 1.15, 210, 30 } }; + return Position{ { default_ } }; + } +}; - LightObserver nullObserver; - LightObserver* observer = &nullObserver; - void setObserver(LightObserver*); +struct LightColor : LightProperty<Color> { + static Color defaultValue() { + return Color::white(); + } +}; + +struct LightIntensity : LightProperty<float> { + static float defaultValue() { + return 0.5; + } +}; - IndexedTuple<LightProperties, LightProperties> properties; +using LightProperties = Properties<LightAnchor, LightPosition, LightColor, LightIntensity>; + +class Light::Impl { +public: + LightProperties::Transitionable properties; }; } // namespace style diff --git a/src/mbgl/style/light_observer.hpp b/src/mbgl/style/light_observer.hpp index 751a84850d..45beb64928 100644 --- a/src/mbgl/style/light_observer.hpp +++ b/src/mbgl/style/light_observer.hpp @@ -1,10 +1,10 @@ #pragma once -#include <mbgl/style/light.hpp> - namespace mbgl { namespace style { +class Light; + class LightObserver { public: virtual ~LightObserver() = default; diff --git a/src/mbgl/style/light_properties.hpp b/src/mbgl/style/light_properties.hpp deleted file mode 100644 index 9f6088a633..0000000000 --- a/src/mbgl/style/light_properties.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include <mbgl/style/property_value.hpp> -#include <mbgl/style/transition_options.hpp> -#include <mbgl/style/types.hpp> -#include <mbgl/style/position.hpp> -#include <mbgl/util/color.hpp> -#include <mbgl/util/indexed_tuple.hpp> - -namespace mbgl { -namespace style { - -template <class T> -class LightProperty { -public: - using Type = T; - using ValueType = PropertyValue<T>; - - PropertyValue<T> value; - TransitionOptions transition; -}; - -struct LightAnchor : LightProperty<LightAnchorType> { - static LightAnchorType defaultValue() { - return LightAnchorType::Viewport; - } -}; - -struct LightPosition : LightProperty<Position> { - static Position defaultValue() { - std::array<float, 3> default_ = { { 1.15, 210, 30 } }; - return Position{ { default_ } }; - } -}; - -struct LightColor : LightProperty<Color> { - static Color defaultValue() { - return Color::white(); - } -}; - -struct LightIntensity : LightProperty<float> { - static float defaultValue() { - return 0.5; - } -}; - -using LightProperties = TypeList<LightAnchor, LightPosition, LightColor, LightIntensity>; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp index 60432334e2..cc6378b366 100644 --- a/src/mbgl/style/observer.hpp +++ b/src/mbgl/style/observer.hpp @@ -1,22 +1,18 @@ #pragma once -#include <mbgl/text/glyph_atlas_observer.hpp> -#include <mbgl/sprite/sprite_atlas_observer.hpp> #include <mbgl/style/source_observer.hpp> -#include <mbgl/map/update.hpp> #include <exception> namespace mbgl { namespace style { -class Observer : public GlyphAtlasObserver, - public SpriteAtlasObserver, - public SourceObserver { +class Observer : public SourceObserver { public: - virtual void onUpdate(Update) {} - virtual void onStyleError(std::exception_ptr) {} + virtual void onStyleLoading() {} virtual void onStyleLoaded() {} + virtual void onUpdate() {} + virtual void onStyleError(std::exception_ptr) {} virtual void onResourceError(std::exception_ptr) {} }; diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp index c203083c49..c4c996b3bd 100644 --- a/src/mbgl/style/paint_property.hpp +++ b/src/mbgl/style/paint_property.hpp @@ -1,108 +1,38 @@ #pragma once -#include <mbgl/style/class_dictionary.hpp> +#include <mbgl/style/properties.hpp> #include <mbgl/style/property_value.hpp> #include <mbgl/style/data_driven_property_value.hpp> -#include <mbgl/style/transition_options.hpp> #include <mbgl/renderer/property_evaluator.hpp> #include <mbgl/renderer/cross_faded_property_evaluator.hpp> #include <mbgl/renderer/data_driven_property_evaluator.hpp> -#include <mbgl/renderer/property_evaluation_parameters.hpp> -#include <mbgl/renderer/cascade_parameters.hpp> -#include <mbgl/renderer/transitioning_property.hpp> -#include <mbgl/renderer/paint_property_binder.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/interpolate.hpp> -#include <mbgl/util/indexed_tuple.hpp> -#include <mbgl/util/ignore.hpp> #include <utility> namespace mbgl { - -class GeometryTileFeature; - namespace style { -template <class Value> -class CascadingPaintProperty { -public: - bool isUndefined() const { - return values.find(ClassID::Default) == values.end(); - } - - const Value& get(const optional<std::string>& klass) const { - static const Value staticValue{}; - const auto it = values.find(klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default); - return it == values.end() ? staticValue : it->second; - } - - void set(const Value& value_, const optional<std::string>& klass) { - values[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = value_; - } - - const TransitionOptions& getTransition(const optional<std::string>& klass) const { - static const TransitionOptions staticValue{}; - const auto it = transitions.find(klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default); - return it == transitions.end() ? staticValue : it->second; - } - - void setTransition(const TransitionOptions& transition, const optional<std::string>& klass) { - transitions[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = transition; - } - - template <class TransitioningProperty> - TransitioningProperty cascade(const CascadeParameters& params, TransitioningProperty prior) const { - TransitionOptions transition; - Value value; - - for (const auto classID : params.classes) { - if (values.find(classID) != values.end()) { - value = values.at(classID); - break; - } - } - - for (const auto classID : params.classes) { - if (transitions.find(classID) != transitions.end()) { - transition = transitions.at(classID).reverseMerge(transition); - break; - } - } - - return TransitioningProperty(std::move(value), - std::move(prior), - transition.reverseMerge(params.transition), - params.now); - } - -private: - std::map<ClassID, Value> values; - std::map<ClassID, TransitionOptions> transitions; -}; - template <class T> class PaintProperty { public: - using ValueType = PropertyValue<T>; - using CascadingType = CascadingPaintProperty<ValueType>; - using UnevaluatedType = TransitioningProperty<ValueType>; + using TransitionableType = Transitionable<PropertyValue<T>>; + using UnevaluatedType = Transitioning<PropertyValue<T>>; using EvaluatorType = PropertyEvaluator<T>; - using EvaluatedType = T; + using PossiblyEvaluatedType = T; + using Type = T; static constexpr bool IsDataDriven = false; }; template <class T, class A, class U> class DataDrivenPaintProperty { public: - using ValueType = DataDrivenPropertyValue<T>; - using CascadingType = CascadingPaintProperty<ValueType>; - using UnevaluatedType = TransitioningProperty<ValueType>; + using TransitionableType = Transitionable<DataDrivenPropertyValue<T>>; + using UnevaluatedType = Transitioning<DataDrivenPropertyValue<T>>; using EvaluatorType = DataDrivenPropertyEvaluator<T>; - using EvaluatedType = PossiblyEvaluatedPropertyValue<T>; + using PossiblyEvaluatedType = PossiblyEvaluatedPropertyValue<T>; + using Type = T; static constexpr bool IsDataDriven = true; - using Type = T; using Attribute = A; using Uniform = U; }; @@ -110,78 +40,13 @@ public: template <class T> class CrossFadedPaintProperty { public: - using ValueType = PropertyValue<T>; - using CascadingType = CascadingPaintProperty<ValueType>; - using UnevaluatedType = TransitioningProperty<ValueType>; + using TransitionableType = Transitionable<PropertyValue<T>>; + using UnevaluatedType = Transitioning<PropertyValue<T>>; using EvaluatorType = CrossFadedPropertyEvaluator<T>; - using EvaluatedType = Faded<T>; + using PossiblyEvaluatedType = Faded<T>; + using Type = T; static constexpr bool IsDataDriven = false; }; -template <class P> -struct IsDataDriven : std::integral_constant<bool, P::IsDataDriven> {}; - -template <class... Ps> -class PaintProperties { -public: - using Properties = TypeList<Ps...>; - using DataDrivenProperties = FilteredTypeList<Properties, IsDataDriven>; - using Binders = PaintPropertyBinders<DataDrivenProperties>; - - using EvaluatedTypes = TypeList<typename Ps::EvaluatedType...>; - using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>; - using CascadingTypes = TypeList<typename Ps::CascadingType...>; - - template <class TypeList> - using Tuple = IndexedTuple<Properties, TypeList>; - - class Evaluated : public Tuple<EvaluatedTypes> { - public: - using Tuple<EvaluatedTypes>::Tuple; - }; - - class Unevaluated : public Tuple<UnevaluatedTypes> { - public: - using Tuple<UnevaluatedTypes>::Tuple; - - bool hasTransition() const { - bool result = false; - util::ignore({ result |= this->template get<Ps>().hasTransition()... }); - return result; - } - - template <class P> - auto evaluate(const PropertyEvaluationParameters& parameters) { - using Evaluator = typename P::EvaluatorType; - - return this->template get<P>().evaluate( - Evaluator(parameters, P::defaultValue()), - parameters.now - ); - } - - Evaluated evaluate(const PropertyEvaluationParameters& parameters) { - return Evaluated { - evaluate<Ps>(parameters)... - }; - } - - }; - - class Cascading : public Tuple<CascadingTypes> { - public: - using Tuple<CascadingTypes>::Tuple; - - Unevaluated cascade(const CascadeParameters& parameters, Unevaluated&& prior) const { - return Unevaluated { - this->template get<Ps>().cascade( - parameters, - std::move(prior.template get<Ps>()) - )... - }; - } - }; -}; - } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index fc3ccf410b..a83897dbf5 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -2,11 +2,13 @@ #include <mbgl/style/layer_impl.hpp> #include <mbgl/style/rapidjson_conversion.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/coordinate.hpp> #include <mbgl/style/conversion/source.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/light.hpp> #include <mbgl/util/logging.hpp> +#include <mbgl/util/string.hpp> #include <mapbox/geojsonvt.hpp> @@ -55,10 +57,12 @@ StyleParseResult Parser::parse(const std::string& json) { if (document.HasMember("center")) { const JSValue& value = document["center"]; - if (value.IsArray() && value.Size() >= 2) { - // Style spec uses lon/lat order - latLng = LatLng(value[1].IsNumber() ? value[1].GetDouble() : 0, - value[0].IsNumber() ? value[0].GetDouble() : 0); + conversion::Error error; + auto convertedLatLng = conversion::convert<LatLng>(value, error); + if (convertedLatLng) { + latLng = *convertedLatLng; + } else { + Log::Warning(Event::ParseStyle, "center coordinate must be a longitude, latitude pair"); } } @@ -83,6 +87,10 @@ StyleParseResult Parser::parse(const std::string& json) { } } + if (document.HasMember("transition")) { + parseTransition(document["transition"]); + } + if (document.HasMember("light")) { parseLight(document["light"]); } @@ -112,6 +120,17 @@ StyleParseResult Parser::parse(const std::string& json) { return nullptr; } +void Parser::parseTransition(const JSValue& value) { + conversion::Error error; + optional<TransitionOptions> converted = conversion::convert<TransitionOptions>(value, error); + if (!converted) { + Log::Warning(Event::ParseStyle, error.message); + return; + } + + transition = std::move(*converted); +} + void Parser::parseLight(const JSValue& value) { conversion::Error error; optional<Light> converted = conversion::convert<Light>(value, error); @@ -236,7 +255,7 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique return; } - layer = reference->baseImpl->cloneRef(id); + layer = reference->cloneRef(id); conversion::setPaintProperties(*layer, value); } else { conversion::Error error; diff --git a/src/mbgl/style/parser.hpp b/src/mbgl/style/parser.hpp index 32b8a7a8bc..401b5ff513 100644 --- a/src/mbgl/style/parser.hpp +++ b/src/mbgl/style/parser.hpp @@ -32,6 +32,7 @@ public: std::vector<std::unique_ptr<Source>> sources; std::vector<std::unique_ptr<Layer>> layers; + TransitionOptions transition; Light light; std::string name; @@ -44,6 +45,7 @@ public: std::vector<FontStack> fontStacks() const; private: + void parseTransition(const JSValue&); void parseLight(const JSValue&); void parseSources(const JSValue&); void parseLayers(const JSValue&); diff --git a/src/mbgl/style/properties.hpp b/src/mbgl/style/properties.hpp new file mode 100644 index 0000000000..dfcf7993a7 --- /dev/null +++ b/src/mbgl/style/properties.hpp @@ -0,0 +1,248 @@ +#pragma once + +#include <mbgl/style/transition_options.hpp> +#include <mbgl/style/conversion/stringify.hpp> +#include <mbgl/renderer/transition_parameters.hpp> +#include <mbgl/renderer/paint_property_binder.hpp> +#include <mbgl/renderer/property_evaluation_parameters.hpp> +#include <mbgl/renderer/transition_parameters.hpp> +#include <mbgl/util/indexed_tuple.hpp> +#include <mbgl/util/ignore.hpp> + +namespace mbgl { + +class GeometryTileFeature; + +namespace style { + +template <class Value> +class Transitioning { +public: + Transitioning() = default; + + explicit Transitioning(Value value_) + : value(std::move(value_)) { + } + + Transitioning(Value value_, + Transitioning<Value> prior_, + TransitionOptions transition, + TimePoint now) + : begin(now + transition.delay.value_or(Duration::zero())), + end(begin + transition.duration.value_or(Duration::zero())), + value(std::move(value_)) { + if (transition.isDefined()) { + prior = { std::move(prior_) }; + } + } + + template <class Evaluator> + auto evaluate(const Evaluator& evaluator, TimePoint now) const { + auto finalValue = value.evaluate(evaluator); + if (!prior) { + // No prior value. + return finalValue; + } else if (now >= end) { + // Transition from prior value is now complete. + prior = {}; + return finalValue; + } else if (value.isDataDriven()) { + // Transitions to data-driven properties are not supported. + // We snap immediately to the data-driven value so that, when we perform layout, + // we see the data-driven function and can use it to populate vertex buffers. + prior = {}; + return finalValue; + } else if (now < begin) { + // Transition hasn't started yet. + return prior->get().evaluate(evaluator, now); + } else { + // Interpolate between recursively-calculated prior value and final. + float t = std::chrono::duration<float>(now - begin) / (end - begin); + return util::interpolate(prior->get().evaluate(evaluator, now), finalValue, + util::DEFAULT_TRANSITION_EASE.solve(t, 0.001)); + } + } + + bool hasTransition() const { + return bool(prior); + } + + bool isUndefined() const { + return value.isUndefined(); + } + + const Value& getValue() const { + return value; + } + +private: + mutable optional<mapbox::util::recursive_wrapper<Transitioning<Value>>> prior; + TimePoint begin; + TimePoint end; + Value value; +}; + +template <class Value> +class Transitionable { +public: + Value value; + TransitionOptions options; + + Transitioning<Value> transition(const TransitionParameters& params, Transitioning<Value> prior) const { + return Transitioning<Value>(value, + std::move(prior), + options.reverseMerge(params.transition), + params.now); + } +}; + +template <class P> +struct IsDataDriven : std::integral_constant<bool, P::IsDataDriven> {}; + +template <class... Ps> +class Properties { +public: + /* + For style properties we implement a two step evaluation process: if you have a zoom level, + you can evaluate a set of unevaluated property values, producing a set of possibly evaluated + values, where undefined, constant, or camera function values have been fully evaluated, and + source or composite function values have not. + + Once you also have a particular feature, you can evaluate that set of possibly evaluated values + fully, producing a set of fully evaluated values. + + This is in theory maximally efficient in terms of avoiding repeated evaluation of camera + functions, though it's more of a historical accident than a purposeful optimization. + */ + + using PropertyTypes = TypeList<Ps...>; + using TransitionableTypes = TypeList<typename Ps::TransitionableType...>; + using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>; + using PossiblyEvaluatedTypes = TypeList<typename Ps::PossiblyEvaluatedType...>; + using EvaluatedTypes = TypeList<typename Ps::Type...>; + + using DataDrivenProperties = FilteredTypeList<PropertyTypes, IsDataDriven>; + using Binders = PaintPropertyBinders<DataDrivenProperties>; + + template <class TypeList> + using Tuple = IndexedTuple<PropertyTypes, TypeList>; + + class Evaluated : public Tuple<EvaluatedTypes> { + public: + template <class... Us> + Evaluated(Us&&... us) + : Tuple<EvaluatedTypes>(std::forward<Us>(us)...) { + } + }; + + class PossiblyEvaluated : public Tuple<PossiblyEvaluatedTypes> { + public: + template <class... Us> + PossiblyEvaluated(Us&&... us) + : Tuple<PossiblyEvaluatedTypes>(std::forward<Us>(us)...) { + } + + template <class T> + static T evaluate(float, const GeometryTileFeature&, const T& t, const T&) { + return t; + } + + template <class T> + static T evaluate(float z, const GeometryTileFeature& feature, + const PossiblyEvaluatedPropertyValue<T>& v, const T& defaultValue) { + return v.match( + [&] (const T& t) { + return t; + }, + [&] (const SourceFunction<T>& t) { + return t.evaluate(feature, defaultValue); + }, + [&] (const CompositeFunction<T>& t) { + return t.evaluate(z, feature, defaultValue); + }); + } + + template <class P> + auto evaluate(float z, const GeometryTileFeature& feature) const { + return evaluate(z, feature, this->template get<P>(), P::defaultValue()); + } + + Evaluated evaluate(float z, const GeometryTileFeature& feature) const { + return Evaluated { + evaluate<Ps>(z, feature)... + }; + } + }; + + class Unevaluated : public Tuple<UnevaluatedTypes> { + public: + template <class... Us> + Unevaluated(Us&&... us) + : Tuple<UnevaluatedTypes>(std::forward<Us>(us)...) { + } + + bool hasTransition() const { + bool result = false; + util::ignore({ result |= this->template get<Ps>().hasTransition()... }); + return result; + } + + template <class P> + auto evaluate(const PropertyEvaluationParameters& parameters) const { + using Evaluator = typename P::EvaluatorType; + return this->template get<P>() + .evaluate(Evaluator(parameters, P::defaultValue()), parameters.now); + } + + PossiblyEvaluated evaluate(const PropertyEvaluationParameters& parameters) const { + return PossiblyEvaluated { + evaluate<Ps>(parameters)... + }; + } + + template <class Writer> + void stringify(Writer& writer) const { + writer.StartObject(); + util::ignore({ (conversion::stringify<Ps>(writer, this->template get<Ps>()), 0)... }); + writer.EndObject(); + } + }; + + class Transitionable : public Tuple<TransitionableTypes> { + public: + template <class... Us> + Transitionable(Us&&... us) + : Tuple<TransitionableTypes>(std::forward<Us>(us)...) { + } + + Unevaluated transitioned(const TransitionParameters& parameters, Unevaluated&& prior) const { + return Unevaluated { + this->template get<Ps>() + .transition(parameters, std::move(prior.template get<Ps>()))... + }; + } + + Unevaluated untransitioned() const { + return Unevaluated { + typename Ps::UnevaluatedType(this->template get<Ps>().value)... + }; + } + + bool hasDataDrivenPropertyDifference(const Transitionable& other) const { + bool result = false; + util::ignore({ (result |= this->template get<Ps>().value.hasDataDrivenPropertyDifference(other.template get<Ps>().value))... }); + return result; + } + }; +}; + +template <class...> +struct ConcatenateProperties; + +template <class... As, class... Bs> +struct ConcatenateProperties<TypeList<As...>, TypeList<Bs...>> { + using Type = Properties<As..., Bs...>; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/rapidjson_conversion.hpp b/src/mbgl/style/rapidjson_conversion.hpp index 101fe67ec0..48a764ccb4 100644 --- a/src/mbgl/style/rapidjson_conversion.hpp +++ b/src/mbgl/style/rapidjson_conversion.hpp @@ -62,6 +62,13 @@ inline optional<float> toNumber(const JSValue& value) { return value.GetDouble(); } +inline optional<double> toDouble(const JSValue& value) { + if (!value.IsNumber()) { + return {}; + } + return value.GetDouble(); +} + inline optional<std::string> toString(const JSValue& value) { if (!value.IsString()) { return {}; diff --git a/src/mbgl/style/source.cpp b/src/mbgl/style/source.cpp index cfb268006b..e7701b8bec 100644 --- a/src/mbgl/style/source.cpp +++ b/src/mbgl/style/source.cpp @@ -1,16 +1,25 @@ #include <mbgl/style/source.hpp> #include <mbgl/style/source_impl.hpp> +#include <mbgl/style/source_observer.hpp> +#include <mbgl/util/logging.hpp> namespace mbgl { namespace style { -Source::Source(SourceType type_, std::unique_ptr<Impl> baseImpl_) - : baseImpl(std::move(baseImpl_)), type(type_) { +static SourceObserver nullObserver; + +Source::Source(Immutable<Impl> impl) + : baseImpl(std::move(impl)), + observer(&nullObserver) { } Source::~Source() = default; -const std::string& Source::getID() const { +SourceType Source::getType() const { + return baseImpl->type; +} + +std::string Source::getID() const { return baseImpl->id; } @@ -18,5 +27,14 @@ optional<std::string> Source::getAttribution() const { return baseImpl->getAttribution(); } +void Source::setObserver(SourceObserver* observer_) { + observer = observer_ ? observer_ : &nullObserver; +} + +void Source::dumpDebugLogs() const { + Log::Info(Event::General, "Source::id: %s", getID().c_str()); + Log::Info(Event::General, "Source::loaded: %d", loaded); +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp index 1e9405abbb..0683f847cb 100644 --- a/src/mbgl/style/source_impl.cpp +++ b/src/mbgl/style/source_impl.cpp @@ -1,26 +1,11 @@ #include <mbgl/style/source_impl.hpp> -#include <mbgl/style/source_observer.hpp> -#include <mbgl/util/logging.hpp> namespace mbgl { namespace style { -static SourceObserver nullObserver; - -Source::Impl::Impl(SourceType type_, std::string id_, Source& base_) +Source::Impl::Impl(SourceType type_, std::string id_) : type(type_), - id(std::move(id_)), - base(base_), - observer(&nullObserver) { -} - -void Source::Impl::dumpDebugLogs() const { - Log::Info(Event::General, "Source::id: %s", base.getID().c_str()); - Log::Info(Event::General, "Source::loaded: %d", loaded); -} - -void Source::Impl::setObserver(SourceObserver* observer_) { - observer = observer_ ? observer_ : &nullObserver; + id(std::move(id_)) { } } // namespace style diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp index 2514ec5120..42da97345a 100644 --- a/src/mbgl/style/source_impl.hpp +++ b/src/mbgl/style/source_impl.hpp @@ -3,35 +3,30 @@ #include <mbgl/style/source.hpp> #include <mbgl/util/noncopyable.hpp> +#include <string> + namespace mbgl { -class FileSource; class RenderSource; namespace style { class SourceObserver; -class Source::Impl : private util::noncopyable { +class Source::Impl { public: - Impl(SourceType, std::string id, Source&); virtual ~Impl() = default; - virtual void loadDescription(FileSource&) = 0; - virtual std::unique_ptr<RenderSource> createRenderSource() const = 0; + Impl& operator=(const Impl&) = delete; - virtual optional<std::string> getAttribution() const { return {}; }; + virtual optional<std::string> getAttribution() const = 0; const SourceType type; const std::string id; - bool loaded = false; - Source& base; - - void setObserver(SourceObserver*); - SourceObserver* observer = nullptr; - - void dumpDebugLogs() const; +protected: + Impl(SourceType, std::string); + Impl(const Impl&) = default; }; } // namespace style diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp index 992f82b1e7..4e3478322d 100644 --- a/src/mbgl/style/sources/geojson_source.cpp +++ b/src/mbgl/style/sources/geojson_source.cpp @@ -1,27 +1,81 @@ #include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/style/sources/geojson_source_impl.hpp> #include <mbgl/style/source_observer.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/util/logging.hpp> namespace mbgl { namespace style { GeoJSONSource::GeoJSONSource(const std::string& id, const GeoJSONOptions& options) - : Source(SourceType::GeoJSON, - std::make_unique<GeoJSONSource::Impl>(std::move(id), *this, options)), - impl(static_cast<Impl*>(baseImpl.get())) { + : Source(makeMutable<Impl>(std::move(id), options)) { } -void GeoJSONSource::setURL(const std::string& url) { - impl->setURL(url); +GeoJSONSource::~GeoJSONSource() = default; + +const GeoJSONSource::Impl& GeoJSONSource::impl() const { + return static_cast<const Impl&>(*baseImpl); +} + +void GeoJSONSource::setURL(const std::string& url_) { + url = std::move(url_); + + // Signal that the source description needs a reload + if (loaded || req) { + loaded = false; + req.reset(); + observer->onSourceDescriptionChanged(*this); + } } void GeoJSONSource::setGeoJSON(const mapbox::geojson::geojson& geoJSON) { - impl->setGeoJSON(geoJSON); - impl->observer->onSourceChanged(*this); + req.reset(); + baseImpl = makeMutable<Impl>(impl(), geoJSON); + observer->onSourceChanged(*this); } optional<std::string> GeoJSONSource::getURL() const { - return impl->getURL(); + return url; +} + +void GeoJSONSource::loadDescription(FileSource& fileSource) { + if (!url) { + loaded = true; + return; + } + + if (req) { + return; + } + + req = fileSource.request(Resource::source(*url), [this](Response res) { + if (res.error) { + observer->onSourceError( + *this, std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified) { + return; + } else if (res.noContent) { + observer->onSourceError( + *this, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON"))); + } else { + conversion::Error error; + optional<GeoJSON> geoJSON = conversion::convertJSON<GeoJSON>(*res.data, error); + if (!geoJSON) { + Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", + error.message.c_str()); + // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for + // tiles to load. + baseImpl = makeMutable<Impl>(impl(), GeoJSON{ FeatureCollection{} }); + } else { + baseImpl = makeMutable<Impl>(impl(), *geoJSON); + } + + loaded = true; + observer->onSourceLoaded(*this); + } + }); } } // namespace style diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index 1a686ff9bc..efa4018b46 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -1,16 +1,13 @@ #include <mbgl/style/sources/geojson_source_impl.hpp> -#include <mbgl/style/conversion/json.hpp> -#include <mbgl/style/conversion/geojson.hpp> -#include <mbgl/style/source_observer.hpp> +#include <mbgl/util/constants.hpp> #include <mbgl/tile/tile_id.hpp> -#include <mbgl/storage/file_source.hpp> -#include <mbgl/renderer/sources/render_geojson_source.hpp> -#include <mbgl/util/constants.cpp> -#include <mbgl/util/logging.hpp> +#include <mbgl/util/string.hpp> #include <mapbox/geojsonvt.hpp> #include <supercluster.hpp> +#include <cmath> + namespace mbgl { namespace style { @@ -42,33 +39,14 @@ private: mapbox::supercluster::Supercluster impl; }; -GeoJSONSource::Impl::Impl(std::string id_, Source& base_, const GeoJSONOptions options_) - : Source::Impl(SourceType::GeoJSON, std::move(id_), base_), options(options_) { -} - -GeoJSONSource::Impl::~Impl() = default; - -void GeoJSONSource::Impl::setURL(std::string url_) { - url = std::move(url_); - - // Signal that the source description needs a reload - if (loaded || req) { - loaded = false; - req.reset(); - observer->onSourceDescriptionChanged(base); - } -} - -optional<std::string> GeoJSONSource::Impl::getURL() const { - return url; -} - -void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) { - req.reset(); - _setGeoJSON(geoJSON); +GeoJSONSource::Impl::Impl(std::string id_, GeoJSONOptions options_) + : Source::Impl(SourceType::GeoJSON, std::move(id_)), + options(std::move(options_)) { } -void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { +GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON) + : Source::Impl(other), + options(other.options) { double scale = util::EXTENT / util::tileSize; if (options.cluster @@ -77,60 +55,20 @@ void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { mapbox::supercluster::Options clusterOptions; clusterOptions.maxZoom = options.clusterMaxZoom; clusterOptions.extent = util::EXTENT; - clusterOptions.radius = std::round(scale * options.clusterRadius); + clusterOptions.radius = ::round(scale * options.clusterRadius); data = std::make_unique<SuperclusterData>( geoJSON.get<mapbox::geometry::feature_collection<double>>(), clusterOptions); } else { mapbox::geojsonvt::Options vtOptions; vtOptions.maxZoom = options.maxzoom; vtOptions.extent = util::EXTENT; - vtOptions.buffer = std::round(scale * options.buffer); + vtOptions.buffer = ::round(scale * options.buffer); vtOptions.tolerance = scale * options.tolerance; data = std::make_unique<GeoJSONVTData>(geoJSON, vtOptions); } } -void GeoJSONSource::Impl::loadDescription(FileSource& fileSource) { - if (!url) { - loaded = true; - return; - } - - if (req) { - return; - } - - req = fileSource.request(Resource::source(*url), [this](Response res) { - if (res.error) { - observer->onSourceError( - base, std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified) { - return; - } else if (res.noContent) { - observer->onSourceError( - base, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON"))); - } else { - conversion::Error error; - optional<GeoJSON> geoJSON = conversion::convertJSON<GeoJSON>(*res.data, error); - if (!geoJSON) { - Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", - error.message.c_str()); - // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for - // tiles to load. - _setGeoJSON(GeoJSON{ FeatureCollection{} }); - } else { - _setGeoJSON(*geoJSON); - } - - loaded = true; - observer->onSourceLoaded(base); - } - }); -} - -std::unique_ptr<RenderSource> GeoJSONSource::Impl::createRenderSource() const { - return std::make_unique<RenderGeoJSONSource>(*this); -} +GeoJSONSource::Impl::~Impl() = default; Range<uint8_t> GeoJSONSource::Impl::getZoomRange() const { return { 0, options.maxzoom }; @@ -140,5 +78,9 @@ GeoJSONData* GeoJSONSource::Impl::getData() const { return data.get(); } +optional<std::string> GeoJSONSource::Impl::getAttribution() const { + return {}; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp index dece1269f8..a524bab9f2 100644 --- a/src/mbgl/style/sources/geojson_source_impl.hpp +++ b/src/mbgl/style/sources/geojson_source_impl.hpp @@ -2,7 +2,7 @@ #include <mbgl/style/source_impl.hpp> #include <mbgl/style/sources/geojson_source.hpp> -#include <mbgl/util/variant.hpp> +#include <mbgl/util/range.hpp> namespace mbgl { @@ -19,25 +19,17 @@ public: class GeoJSONSource::Impl : public Source::Impl { public: - Impl(std::string id, Source&, const GeoJSONOptions); + Impl(std::string id, GeoJSONOptions); + Impl(const GeoJSONSource::Impl&, const GeoJSON&); ~Impl() final; - void setURL(std::string); - optional<std::string> getURL() const; Range<uint8_t> getZoomRange() const; - - void setGeoJSON(const GeoJSON&); GeoJSONData* getData() const; - void loadDescription(FileSource&) final; - std::unique_ptr<RenderSource> createRenderSource() const final; + optional<std::string> getAttribution() const final; private: - void _setGeoJSON(const GeoJSON&); - GeoJSONOptions options; - optional<std::string> url; - std::unique_ptr<AsyncRequest> req; std::unique_ptr<GeoJSONData> data; }; diff --git a/src/mbgl/style/sources/image_source.cpp b/src/mbgl/style/sources/image_source.cpp new file mode 100644 index 0000000000..9b60ba1a48 --- /dev/null +++ b/src/mbgl/style/sources/image_source.cpp @@ -0,0 +1,84 @@ +#include <mbgl/style/sources/image_source.hpp> +#include <mbgl/style/sources/image_source_impl.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/style/source_observer.hpp> +#include <mbgl/util/premultiply.hpp> +#include <mbgl/storage/file_source.hpp> + +namespace mbgl { +namespace style { + +ImageSource::ImageSource(std::string id, const std::array<LatLng, 4> coords_) + : Source(makeMutable<Impl>(std::move(id), coords_)) { +} + +ImageSource::~ImageSource() = default; + +const ImageSource::Impl& ImageSource::impl() const { + return static_cast<const Impl&>(*baseImpl); +} + +void ImageSource::setCoordinates(const std::array<LatLng, 4>& coords_) { + baseImpl = makeMutable<Impl>(impl(), coords_); + observer->onSourceChanged(*this); +} + +std::array<LatLng, 4> ImageSource::getCoordinates() const { + return impl().getCoordinates(); +} + +void ImageSource::setURL(const std::string& url_) { + url = std::move(url_); + // Signal that the source description needs a reload + if (loaded || req) { + loaded = false; + req.reset(); + observer->onSourceDescriptionChanged(*this); + } +} + +void ImageSource::setImage(PremultipliedImage&& image_) { + url = {}; + if (req) { + req.reset(); + } + loaded = true; + baseImpl = makeMutable<Impl>(impl(), std::move(image_)); + observer->onSourceChanged(*this); +} + +optional<std::string> ImageSource::getURL() const { + return url; +} + +void ImageSource::loadDescription(FileSource& fileSource) { + if (!url) { + loaded = true; + } + + if (req || loaded) { + return; + } + const Resource imageResource { Resource::Image, *url, {}, Resource::Necessity::Required }; + + req = fileSource.request(imageResource, [this](Response res) { + if (res.error) { + observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified) { + return; + } else if (res.noContent) { + observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty image url"))); + } else { + try { + baseImpl = makeMutable<Impl>(impl(), decodeImage(*res.data)); + } catch (...) { + observer->onSourceError(*this, std::current_exception()); + } + loaded = true; + observer->onSourceLoaded(*this); + } + }); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/image_source_impl.cpp b/src/mbgl/style/sources/image_source_impl.cpp new file mode 100644 index 0000000000..c1f31dbdc6 --- /dev/null +++ b/src/mbgl/style/sources/image_source_impl.cpp @@ -0,0 +1,38 @@ +#include <mbgl/style/sources/image_source_impl.hpp> +#include <mbgl/util/geo.hpp> + +namespace mbgl { +namespace style { + +ImageSource::Impl::Impl(std::string id_, std::array<LatLng, 4> coords_) + : Source::Impl(SourceType::Image, std::move(id_)), + coords(std::move(coords_)) { +} + +ImageSource::Impl::Impl(const Impl& other, std::array<LatLng, 4> coords_) + : Source::Impl(other), + coords(std::move(coords_)), + image(other.image) { +} + +ImageSource::Impl::Impl(const Impl& rhs, PremultipliedImage&& image_) + : Source::Impl(rhs), + coords(rhs.coords), + image(std::make_shared<PremultipliedImage>(std::move(image_))) { +} +ImageSource::Impl::~Impl() = default; + +std::shared_ptr<PremultipliedImage> ImageSource::Impl::getImage() const { + return image; +} + +std::array<LatLng, 4> ImageSource::Impl::getCoordinates() const { + return coords; +} + +optional<std::string> ImageSource::Impl::getAttribution() const { + return {}; +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/image_source_impl.hpp b/src/mbgl/style/sources/image_source_impl.hpp new file mode 100644 index 0000000000..1e1b005a32 --- /dev/null +++ b/src/mbgl/style/sources/image_source_impl.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <mbgl/style/source_impl.hpp> +#include <mbgl/style/sources/image_source.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/geo.hpp> + +namespace mbgl { + +namespace style { + +class ImageSource::Impl : public Source::Impl { +public: + Impl(std::string id, std::array<LatLng, 4> coords); + Impl(const Impl& rhs, std::array<LatLng, 4> coords); + Impl(const Impl& rhs, PremultipliedImage&& image); + + ~Impl() final; + + std::shared_ptr<PremultipliedImage> getImage() const; + std::array<LatLng, 4> getCoordinates() const; + + optional<std::string> getAttribution() const final; +private: + std::array<LatLng, 4> coords; + std::shared_ptr<PremultipliedImage> image; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/raster_source.cpp b/src/mbgl/style/sources/raster_source.cpp index 94fdbcef12..0a0412a4ed 100644 --- a/src/mbgl/style/sources/raster_source.cpp +++ b/src/mbgl/style/sources/raster_source.cpp @@ -1,21 +1,81 @@ #include <mbgl/style/sources/raster_source.hpp> #include <mbgl/style/sources/raster_source_impl.hpp> +#include <mbgl/style/source_observer.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion/tileset.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/util/mapbox.hpp> namespace mbgl { namespace style { -RasterSource::RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize) - : Source(SourceType::Raster, std::make_unique<RasterSource::Impl>(std::move(id), *this, std::move(urlOrTileset), tileSize)), - impl(static_cast<Impl*>(baseImpl.get())) { +RasterSource::RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset_, uint16_t tileSize) + : Source(makeMutable<Impl>(std::move(id), tileSize)), + urlOrTileset(std::move(urlOrTileset_)) { +} + +RasterSource::~RasterSource() = default; + +const RasterSource::Impl& RasterSource::impl() const { + return static_cast<const Impl&>(*baseImpl); +} + +const variant<std::string, Tileset>& RasterSource::getURLOrTileset() const { + return urlOrTileset; } optional<std::string> RasterSource::getURL() const { - auto urlOrTileset = impl->getURLOrTileset(); - if (urlOrTileset.is<std::string>()) { - return urlOrTileset.get<std::string>(); - } else { + if (urlOrTileset.is<Tileset>()) { return {}; } + + return urlOrTileset.get<std::string>(); +} + +uint16_t RasterSource::getTileSize() const { + return impl().getTileSize(); +} + +void RasterSource::loadDescription(FileSource& fileSource) { + if (urlOrTileset.is<Tileset>()) { + baseImpl = makeMutable<Impl>(impl(), urlOrTileset.get<Tileset>()); + loaded = true; + return; + } + + if (req) { + return; + } + + const std::string& url = urlOrTileset.get<std::string>(); + req = fileSource.request(Resource::source(url), [this, url](Response res) { + if (res.error) { + observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified) { + return; + } else if (res.noContent) { + observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON"))); + } else { + conversion::Error error; + optional<Tileset> tileset = conversion::convertJSON<Tileset>(*res.data, error); + if (!tileset) { + observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(error.message))); + return; + } + + util::mapbox::canonicalizeTileset(*tileset, url, getType(), getTileSize()); + bool changed = impl().getTileset() != *tileset; + + baseImpl = makeMutable<Impl>(impl(), *tileset); + loaded = true; + + observer->onSourceLoaded(*this); + + if (changed) { + observer->onSourceChanged(*this); + } + } + }); } } // namespace style diff --git a/src/mbgl/style/sources/raster_source_impl.cpp b/src/mbgl/style/sources/raster_source_impl.cpp index b85d221f2e..50dae1f07e 100644 --- a/src/mbgl/style/sources/raster_source_impl.cpp +++ b/src/mbgl/style/sources/raster_source_impl.cpp @@ -1,17 +1,32 @@ #include <mbgl/style/sources/raster_source_impl.hpp> -#include <mbgl/renderer/sources/render_raster_source.hpp> namespace mbgl { namespace style { -RasterSource::Impl::Impl(std::string id_, Source& base_, - variant<std::string, Tileset> urlOrTileset_, - uint16_t tileSize_) - : TileSourceImpl(SourceType::Raster, std::move(id_), base_, std::move(urlOrTileset_), tileSize_) { +RasterSource::Impl::Impl(std::string id_, uint16_t tileSize_) + : Source::Impl(SourceType::Raster, std::move(id_)), + tileSize(tileSize_) { } -std::unique_ptr<RenderSource> RasterSource::Impl::createRenderSource() const { - return std::make_unique<RenderRasterSource>(*this); +RasterSource::Impl::Impl(const Impl& other, Tileset tileset_) + : Source::Impl(other), + tileSize(other.tileSize), + tileset(std::move(tileset_)) { +} + +uint16_t RasterSource::Impl::getTileSize() const { + return tileSize; +} + +optional<Tileset> RasterSource::Impl::getTileset() const { + return tileset; +} + +optional<std::string> RasterSource::Impl::getAttribution() const { + if (!tileset) { + return {}; + } + return tileset->attribution; } } // namespace style diff --git a/src/mbgl/style/sources/raster_source_impl.hpp b/src/mbgl/style/sources/raster_source_impl.hpp index 4bc76560f8..c41d5485b2 100644 --- a/src/mbgl/style/sources/raster_source_impl.hpp +++ b/src/mbgl/style/sources/raster_source_impl.hpp @@ -1,16 +1,24 @@ #pragma once #include <mbgl/style/sources/raster_source.hpp> -#include <mbgl/style/tile_source_impl.hpp> +#include <mbgl/style/source_impl.hpp> namespace mbgl { namespace style { -class RasterSource::Impl : public TileSourceImpl { +class RasterSource::Impl : public Source::Impl { public: - Impl(std::string id, Source&, variant<std::string, Tileset>, uint16_t tileSize); + Impl(std::string id, uint16_t tileSize); + Impl(const Impl&, Tileset); - std::unique_ptr<RenderSource> createRenderSource() const final; + optional<Tileset> getTileset() const; + uint16_t getTileSize() const; + + optional<std::string> getAttribution() const final; + +private: + uint16_t tileSize; + optional<Tileset> tileset; }; } // namespace style diff --git a/src/mbgl/style/sources/vector_source.cpp b/src/mbgl/style/sources/vector_source.cpp index 4bcd3b8985..ccdd453c75 100644 --- a/src/mbgl/style/sources/vector_source.cpp +++ b/src/mbgl/style/sources/vector_source.cpp @@ -1,21 +1,78 @@ #include <mbgl/style/sources/vector_source.hpp> #include <mbgl/style/sources/vector_source_impl.hpp> +#include <mbgl/style/source_observer.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion/tileset.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/util/mapbox.hpp> +#include <mbgl/util/constants.hpp> namespace mbgl { namespace style { -VectorSource::VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset) - : Source(SourceType::Vector, std::make_unique<VectorSource::Impl>(std::move(id), *this, std::move(urlOrTileset))), - impl(static_cast<Impl*>(baseImpl.get())) { +VectorSource::VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset_) + : Source(makeMutable<Impl>(std::move(id))), + urlOrTileset(std::move(urlOrTileset_)) { +} + +VectorSource::~VectorSource() = default; + +const VectorSource::Impl& VectorSource::impl() const { + return static_cast<const Impl&>(*baseImpl); +} + +const variant<std::string, Tileset>& VectorSource::getURLOrTileset() const { + return urlOrTileset; } optional<std::string> VectorSource::getURL() const { - auto urlOrTileset = impl->getURLOrTileset(); - if (urlOrTileset.is<std::string>()) { - return urlOrTileset.get<std::string>(); - } else { + if (urlOrTileset.is<Tileset>()) { return {}; } + + return urlOrTileset.get<std::string>(); +} + +void VectorSource::loadDescription(FileSource& fileSource) { + if (urlOrTileset.is<Tileset>()) { + baseImpl = makeMutable<Impl>(impl(), urlOrTileset.get<Tileset>()); + loaded = true; + return; + } + + if (req) { + return; + } + + const std::string& url = urlOrTileset.get<std::string>(); + req = fileSource.request(Resource::source(url), [this, url](Response res) { + if (res.error) { + observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified) { + return; + } else if (res.noContent) { + observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON"))); + } else { + conversion::Error error; + optional<Tileset> tileset = conversion::convertJSON<Tileset>(*res.data, error); + if (!tileset) { + observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(error.message))); + return; + } + + util::mapbox::canonicalizeTileset(*tileset, url, getType(), util::tileSize); + bool changed = impl().getTileset() != *tileset; + + baseImpl = makeMutable<Impl>(impl(), *tileset); + loaded = true; + + observer->onSourceLoaded(*this); + + if (changed) { + observer->onSourceChanged(*this); + } + } + }); } } // namespace style diff --git a/src/mbgl/style/sources/vector_source_impl.cpp b/src/mbgl/style/sources/vector_source_impl.cpp index 158abf8575..b06f0557bf 100644 --- a/src/mbgl/style/sources/vector_source_impl.cpp +++ b/src/mbgl/style/sources/vector_source_impl.cpp @@ -1,16 +1,26 @@ #include <mbgl/style/sources/vector_source_impl.hpp> -#include <mbgl/renderer/sources/render_vector_source.hpp> -#include <mbgl/util/constants.hpp> namespace mbgl { namespace style { -VectorSource::Impl::Impl(std::string id_, Source& base_, variant<std::string, Tileset> urlOrTileset_) - : TileSourceImpl(SourceType::Vector, std::move(id_), base_, std::move(urlOrTileset_), util::tileSize) { +VectorSource::Impl::Impl(std::string id_) + : Source::Impl(SourceType::Vector, std::move(id_)) { } -std::unique_ptr<RenderSource> VectorSource::Impl::createRenderSource() const { - return std::make_unique<RenderVectorSource>(*this); +VectorSource::Impl::Impl(const Impl& other, Tileset tileset_) + : Source::Impl(other), + tileset(std::move(tileset_)) { +} + +optional<Tileset> VectorSource::Impl::getTileset() const { + return tileset; +} + +optional<std::string> VectorSource::Impl::getAttribution() const { + if (!tileset) { + return {}; + } + return tileset->attribution; } } // namespace style diff --git a/src/mbgl/style/sources/vector_source_impl.hpp b/src/mbgl/style/sources/vector_source_impl.hpp index 844739948c..5e559b9266 100644 --- a/src/mbgl/style/sources/vector_source_impl.hpp +++ b/src/mbgl/style/sources/vector_source_impl.hpp @@ -1,16 +1,22 @@ #pragma once #include <mbgl/style/sources/vector_source.hpp> -#include <mbgl/style/tile_source_impl.hpp> +#include <mbgl/style/source_impl.hpp> namespace mbgl { namespace style { -class VectorSource::Impl : public TileSourceImpl { +class VectorSource::Impl : public Source::Impl { public: - Impl(std::string id, Source&, variant<std::string, Tileset>); + Impl(std::string id); + Impl(const Impl&, Tileset); - std::unique_ptr<RenderSource> createRenderSource() const final; + optional<Tileset> getTileset() const; + + optional<std::string> getAttribution() const final; + +private: + optional<Tileset> tileset; }; } // namespace style diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index f1ac22082b..bd8631fc52 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -1,813 +1,133 @@ #include <mbgl/style/style.hpp> -#include <mbgl/style/observer.hpp> -#include <mbgl/style/source_impl.hpp> -#include <mbgl/style/layers/symbol_layer.hpp> -#include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/style/layers/custom_layer.hpp> -#include <mbgl/style/layers/custom_layer_impl.hpp> -#include <mbgl/style/layers/background_layer.hpp> -#include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/style/layers/fill_layer.hpp> -#include <mbgl/style/layers/fill_extrusion_layer.hpp> -#include <mbgl/style/layers/line_layer.hpp> -#include <mbgl/style/layers/circle_layer.hpp> -#include <mbgl/style/layers/raster_layer.hpp> -#include <mbgl/style/layer_impl.hpp> -#include <mbgl/style/parser.hpp> -#include <mbgl/style/transition_options.hpp> -#include <mbgl/style/class_dictionary.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/text/glyph_atlas.hpp> -#include <mbgl/geometry/line_atlas.hpp> -#include <mbgl/renderer/update_parameters.hpp> -#include <mbgl/renderer/cascade_parameters.hpp> -#include <mbgl/renderer/property_evaluation_parameters.hpp> -#include <mbgl/renderer/tile_parameters.hpp> -#include <mbgl/renderer/render_source.hpp> -#include <mbgl/renderer/render_item.hpp> -#include <mbgl/renderer/render_tile.hpp> -#include <mbgl/renderer/render_background_layer.hpp> -#include <mbgl/renderer/render_circle_layer.hpp> -#include <mbgl/renderer/render_custom_layer.hpp> -#include <mbgl/renderer/render_fill_extrusion_layer.hpp> -#include <mbgl/renderer/render_fill_layer.hpp> -#include <mbgl/renderer/render_line_layer.hpp> -#include <mbgl/renderer/render_raster_layer.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> -#include <mbgl/tile/tile.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/exception.hpp> -#include <mbgl/util/geometry.hpp> -#include <mbgl/util/string.hpp> -#include <mbgl/util/logging.hpp> -#include <mbgl/util/math.hpp> -#include <mbgl/util/std.hpp> -#include <mbgl/math/minmax.hpp> -#include <mbgl/map/query.hpp> - -#include <algorithm> +#include <mbgl/style/style_impl.hpp> +#include <mbgl/style/light.hpp> +#include <mbgl/style/image.hpp> +#include <mbgl/style/source.hpp> +#include <mbgl/style/layer.hpp> namespace mbgl { namespace style { -static Observer nullObserver; - -struct QueueSourceReloadVisitor { - UpdateBatch& updateBatch; - - // No need to reload sources for these types; their visibility can change but - // they don't participate in layout. - void operator()(CustomLayer&) {} - void operator()(RasterLayer&) {} - void operator()(BackgroundLayer&) {} - - template <class VectorLayer> - void operator()(VectorLayer& layer) { - updateBatch.sourceIDs.insert(layer.getSourceID()); - } -}; - -Style::Style(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio) - : scheduler(scheduler_), - fileSource(fileSource_), - glyphAtlas(std::make_unique<GlyphAtlas>(Size{ 2048, 2048 }, fileSource)), - spriteAtlas(std::make_unique<SpriteAtlas>(Size{ 1024, 1024 }, pixelRatio)), - lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })), - light(std::make_unique<Light>()), - renderLight(std::make_unique<RenderLight>(light->impl)), - observer(&nullObserver) { - glyphAtlas->setObserver(this); - spriteAtlas->setObserver(this); - light->impl->setObserver(this); +Style::Style(Scheduler& scheduler, FileSource& fileSource, float pixelRatio) + : impl(std::make_unique<Impl>(scheduler, fileSource, pixelRatio)) { } -Style::~Style() { - for (const auto& source : sources) { - source->baseImpl->setObserver(nullptr); - } - - for (const auto& layer : layers) { - if (CustomLayer* customLayer = layer->as<CustomLayer>()) { - customLayer->impl->deinitialize(); - } - } +Style::~Style() = default; - glyphAtlas->setObserver(nullptr); - spriteAtlas->setObserver(nullptr); - light->impl->setObserver(nullptr); +void Style::loadJSON(const std::string& json) { + impl->loadJSON(json); } -bool Style::addClass(const std::string& className) { - if (hasClass(className)) return false; - classes.push_back(className); - return true; +void Style::loadURL(const std::string& url) { + impl->loadURL(url); } -bool Style::hasClass(const std::string& className) const { - return std::find(classes.begin(), classes.end(), className) != classes.end(); +std::string Style::getJSON() const { + return impl->getJSON(); } -bool Style::removeClass(const std::string& className) { - const auto it = std::find(classes.begin(), classes.end(), className); - if (it != classes.end()) { - classes.erase(it); - return true; - } - return false; +std::string Style::getURL() const { + return impl->getURL(); } -void Style::setClasses(const std::vector<std::string>& classNames) { - classes = classNames; +std::string Style::getName() const { + return impl->getName(); } -std::vector<std::string> Style::getClasses() const { - return classes; -} - -void Style::setTransitionOptions(const TransitionOptions& options) { - transitionOptions = options; +CameraOptions Style::getDefaultCamera() const { + return impl->getDefaultCamera(); } TransitionOptions Style::getTransitionOptions() const { - return transitionOptions; -} - -void Style::setJSON(const std::string& json) { - sources.clear(); - renderSources.clear(); - layers.clear(); - renderLayers.clear(); - classes.clear(); - transitionOptions = {}; - updateBatch = {}; - - Parser parser; - auto error = parser.parse(json); - - if (error) { - std::string message = "Failed to parse style: " + util::toString(error); - Log::Error(Event::ParseStyle, message.c_str()); - observer->onStyleError(std::make_exception_ptr(util::StyleParseException(message))); - observer->onResourceError(error); - return; - } - - for (auto& source : parser.sources) { - addSource(std::move(source)); - } - - for (auto& layer : parser.layers) { - addLayer(std::move(layer)); - } - - name = parser.name; - defaultLatLng = parser.latLng; - defaultZoom = parser.zoom; - defaultBearing = parser.bearing; - defaultPitch = parser.pitch; - setLight(std::make_unique<Light>(parser.light)); - - glyphAtlas->setURL(parser.glyphURL); - spriteAtlas->load(parser.spriteURL, scheduler, fileSource); - - loaded = true; - - observer->onStyleLoaded(); -} - -void Style::addSource(std::unique_ptr<Source> source) { - // Guard against duplicate source ids - auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& existing) { - return existing->getID() == source->getID(); - }); - - if (it != sources.end()) { - std::string msg = "Source " + source->getID() + " already exists"; - throw std::runtime_error(msg.c_str()); - } - - source->baseImpl->setObserver(this); - source->baseImpl->loadDescription(fileSource); - - std::unique_ptr<RenderSource> renderSource = source->baseImpl->createRenderSource(); - renderSource->setObserver(this); - renderSources.emplace_back(std::move(renderSource)); - - sources.emplace_back(std::move(source)); -} - -struct SourceIdUsageEvaluator { - const std::string& sourceId; - - bool operator()(BackgroundLayer&) { return false; } - bool operator()(CustomLayer&) { return false; } - - template <class LayerType> - bool operator()(LayerType& layer) { - return layer.getSourceID() == sourceId; - } -}; - -std::unique_ptr<Source> Style::removeSource(const std::string& id) { - // Check if source is in use - SourceIdUsageEvaluator sourceIdEvaluator {id}; - auto layerIt = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { - return layer->accept(sourceIdEvaluator); - }); - - if (layerIt != layers.end()) { - Log::Warning(Event::General, "Source '%s' is in use, cannot remove", id.c_str()); - return nullptr; - } - - auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) { - return source->getID() == id; - }); - - if (it == sources.end()) { - return nullptr; - } - - util::erase_if(renderSources, [&](const auto& source) { - return source->baseImpl.id == id; - }); - - auto source = std::move(*it); - source->baseImpl->setObserver(nullptr); - sources.erase(it); - updateBatch.sourceIDs.erase(id); - - return source; -} - -std::vector<const Layer*> Style::getLayers() const { - std::vector<const Layer*> result; - result.reserve(layers.size()); - for (const auto& layer : layers) { - result.push_back(layer.get()); - } - return result; -} - -std::vector<Layer*> Style::getLayers() { - std::vector<Layer*> result; - result.reserve(layers.size()); - for (auto& layer : layers) { - result.push_back(layer.get()); - } - return result; -} - -std::vector<std::unique_ptr<Layer>>::const_iterator Style::findLayer(const std::string& id) const { - return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { - return layer->baseImpl->id == id; - }); -} - -Layer* Style::getLayer(const std::string& id) const { - auto it = findLayer(id); - return it != layers.end() ? it->get() : nullptr; -} - -Layer* Style::addLayer(std::unique_ptr<Layer> layer, optional<std::string> before) { - // TODO: verify source - - // Guard against duplicate layer ids - auto it = std::find_if(layers.begin(), layers.end(), [&](const auto& existing) { - return existing->getID() == layer->getID(); - }); - - if (it != layers.end()) { - throw std::runtime_error(std::string{"Layer "} + layer->getID() + " already exists"); - } - - if (CustomLayer* customLayer = layer->as<CustomLayer>()) { - customLayer->impl->initialize(); - } - - layer->baseImpl->setObserver(this); - layer->accept(QueueSourceReloadVisitor { updateBatch }); - - auto added = layers.emplace(before ? findLayer(*before) : layers.end(), std::move(layer))->get(); - renderLayers.emplace(before ? findRenderLayer(*before) : renderLayers.end(), added->baseImpl->createRenderLayer()); - return std::move(added); -} - -std::unique_ptr<Layer> Style::removeLayer(const std::string& id) { - auto it = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { - return layer->baseImpl->id == id; - }); - - if (it == layers.end()) - return nullptr; - - auto layer = std::move(*it); - - if (CustomLayer* customLayer = layer->as<CustomLayer>()) { - customLayer->impl->deinitialize(); - } - - layer->baseImpl->setObserver(nullptr); - layers.erase(it); - removeRenderLayer(id); - return layer; -} - -std::vector<const RenderLayer*> Style::getRenderLayers() const { - std::vector<const RenderLayer*> result; - result.reserve(renderLayers.size()); - for (const auto& layer : renderLayers) { - result.push_back(layer.get()); - } - return result; -} - -std::vector<RenderLayer*> Style::getRenderLayers() { - std::vector<RenderLayer*> result; - result.reserve(renderLayers.size()); - for (auto& layer : renderLayers) { - result.push_back(layer.get()); - } - return result; -} - -std::vector<std::unique_ptr<RenderLayer>>::const_iterator Style::findRenderLayer(const std::string& id) const { - return std::find_if(renderLayers.begin(), renderLayers.end(), [&](const auto& layer) { - return layer->baseImpl.id == id; - }); -} - -RenderLayer* Style::getRenderLayer(const std::string& id) const { - auto it = findRenderLayer(id); - return it != renderLayers.end() ? it->get() : nullptr; -} - -void Style::removeRenderLayer(const std::string& id) { - auto it = std::find_if(renderLayers.begin(), renderLayers.end(), [&](const auto& layer) { - return layer->baseImpl.id == id; - }); - - if (it != renderLayers.end()) { - renderLayers.erase(it); - } -} - -void Style::setLight(std::unique_ptr<Light> light_) { - light = std::move(light_); - light->impl->setObserver(this); - - // Copy renderlight to preserve the initialised - // transitioning light properties - renderLight = renderLight->copy(light->impl); - - onLightChanged(*light); -} - -Light* Style::getLight() const { - return light.get(); -} - -RenderLight* Style::getRenderLight() const { - return renderLight.get(); + return impl->getTransitionOptions(); } -std::string Style::getName() const { - return name; +void Style::setTransitionOptions(const TransitionOptions& options) { + impl->mutated = true; + impl->setTransitionOptions(options); } -LatLng Style::getDefaultLatLng() const { - return defaultLatLng; +void Style::setLight(std::unique_ptr<Light> light) { + impl->setLight(std::move(light)); } -double Style::getDefaultZoom() const { - return defaultZoom; +Light* Style::getLight() { + impl->mutated = true; + return impl->getLight(); } -double Style::getDefaultBearing() const { - return defaultBearing; +const Light* Style::getLight() const { + return impl->getLight(); } -double Style::getDefaultPitch() const { - return defaultPitch; +const Image* Style::getImage(const std::string& name) const { + return impl->getImage(name); } -void Style::update(const UpdateParameters& parameters) { - bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint); - - std::vector<ClassID> classIDs; - for (const auto& className : classes) { - classIDs.push_back(ClassDictionary::Get().lookup(className)); - } - classIDs.push_back(ClassID::Default); - - const CascadeParameters cascadeParameters { - classIDs, - parameters.timePoint, - parameters.mode == MapMode::Continuous ? transitionOptions : TransitionOptions() - }; - - const PropertyEvaluationParameters evaluationParameters { - zoomHistory, - parameters.timePoint, - parameters.mode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Duration::zero() - }; - - const TileParameters tileParameters(parameters.pixelRatio, - parameters.debugOptions, - parameters.transformState, - parameters.scheduler, - parameters.fileSource, - parameters.mode, - parameters.annotationManager, - *this); - - const bool cascade = parameters.updateFlags & Update::Classes; - const bool evaluate = cascade || zoomChanged || parameters.updateFlags & Update::RecalculateStyle; - - if (cascade) { - renderLight->transition(cascadeParameters); - } - - if (evaluate || renderLight->hasTransition()) { - renderLight->evaluate(evaluationParameters); - } - - for (const auto& renderSource : renderSources) { - renderSource->enabled = false; - } - - for (const auto& layer : renderLayers) { - if (cascade) { - layer->cascade(cascadeParameters); - } - - if (evaluate || layer->hasTransition()) { - layer->evaluate(evaluationParameters); - } - - if (layer->needsRendering(zoomHistory.lastZoom)) { - if (RenderSource* renderSource = getRenderSource(layer->baseImpl.source)) { - renderSource->enabled = true; - } - } - } - - for (const auto& renderSource : renderSources) { - bool updated = updateBatch.sourceIDs.count(renderSource->baseImpl.id); - if (renderSource->enabled) { - if (updated) { - renderSource->reloadTiles(); - } - renderSource->updateTiles(tileParameters); - } else if (updated) { - renderSource->invalidateTiles(); - } else { - renderSource->removeTiles(); - } - } - - updateBatch.sourceIDs.clear(); +void Style::addImage(std::unique_ptr<Image> image) { + impl->mutated = true; + impl->addImage(std::move(image)); } -std::vector<const Source*> Style::getSources() const { - std::vector<const Source*> result; - result.reserve(sources.size()); - for (const auto& source : sources) { - result.push_back(source.get()); - } - return result; +void Style::removeImage(const std::string& name) { + impl->mutated = true; + impl->removeImage(name); } std::vector<Source*> Style::getSources() { - std::vector<Source*> result; - result.reserve(sources.size()); - for (auto& source : sources) { - result.push_back(source.get()); - } - return result; + impl->mutated = true; + return impl->getSources(); } -Source* Style::getSource(const std::string& id) const { - const auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) { - return source->getID() == id; - }); - - return it != sources.end() ? it->get() : nullptr; -} - -RenderSource* Style::getRenderSource(const std::string& id) const { - const auto it = std::find_if(renderSources.begin(), renderSources.end(), [&](const auto& source) { - return source->baseImpl.id == id; - }); - - return it != renderSources.end() ? it->get() : nullptr; -} - -bool Style::hasTransitions() const { - if (renderLight->hasTransition()) { - return true; - } - - for (const auto& layer : renderLayers) { - if (layer->hasTransition()) { - return true; - } - } - - return false; -} - -bool Style::isLoaded() const { - if (!loaded) { - return false; - } - - for (const auto& source: sources) { - if (!source->baseImpl->loaded) { - return false; - } - } - - for (const auto& renderSource: renderSources) { - if (!renderSource->isLoaded()) { - return false; - } - } - - if (!spriteAtlas->isLoaded()) { - return false; - } - - return true; -} - -RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const { - RenderData result; - - for (const auto& renderSource: renderSources) { - if (renderSource->enabled) { - result.sources.insert(renderSource.get()); - } - } - - for (const auto& layer : renderLayers) { - if (!layer->needsRendering(zoomHistory.lastZoom)) { - continue; - } - - if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) { - if (debugOptions & MapDebugOptions::Overdraw) { - // We want to skip glClear optimization in overdraw mode. - result.order.emplace_back(*layer); - continue; - } - const BackgroundPaintProperties::Evaluated& paint = background->evaluated; - if (layer.get() == renderLayers[0].get() && paint.get<BackgroundPattern>().from.empty()) { - // This is a solid background. We can use glClear(). - result.backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>(); - } else { - // This is a textured background, or not the bottommost layer. We need to render it with a quad. - result.order.emplace_back(*layer); - } - continue; - } - - if (layer->is<RenderCustomLayer>()) { - result.order.emplace_back(*layer); - continue; - } - - RenderSource* source = getRenderSource(layer->baseImpl.source); - if (!source) { - Log::Warning(Event::Render, "can't find source for layer '%s'", layer->baseImpl.id.c_str()); - continue; - } - - auto& renderTiles = source->getRenderTiles(); - const bool symbolLayer = layer->is<RenderSymbolLayer>(); - - // Sort symbol tiles in opposite y position, so tiles with overlapping - // symbols are drawn on top of each other, with lower symbols being - // drawn on top of higher symbols. - std::vector<std::reference_wrapper<RenderTile>> sortedTiles; - std::transform(renderTiles.begin(), renderTiles.end(), std::back_inserter(sortedTiles), - [](auto& pair) { return std::ref(pair.second); }); - if (symbolLayer) { - std::sort(sortedTiles.begin(), sortedTiles.end(), - [angle](const RenderTile& a, const RenderTile& b) { - Point<float> pa(a.id.canonical.x, a.id.canonical.y); - Point<float> pb(b.id.canonical.x, b.id.canonical.y); - - auto par = util::rotate(pa, angle); - auto pbr = util::rotate(pb, angle); - - return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x); - }); - } - - std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion; - for (auto tileIt = sortedTiles.begin(); tileIt != sortedTiles.end(); ++tileIt) { - auto& tile = tileIt->get(); - if (!tile.tile.isRenderable()) { - continue; - } - - // We're not clipping symbol layers, so when we have both parents and children of symbol - // layers, we drop all children in favor of their parent to avoid duplicate labels. - // See https://github.com/mapbox/mapbox-gl-native/issues/2482 - if (symbolLayer) { - bool skip = false; - // Look back through the buckets we decided to render to find out whether there is - // already a bucket from this layer that is a parent of this tile. Tiles are ordered - // by zoom level when we obtain them from getTiles(). - for (auto it = sortedTilesForInsertion.rbegin(); - it != sortedTilesForInsertion.rend(); ++it) { - if (tile.tile.id.isChildOf(it->get().tile.id)) { - skip = true; - break; - } - } - if (skip) { - continue; - } - } - - auto bucket = tile.tile.getBucket(*layer); - if (bucket) { - sortedTilesForInsertion.emplace_back(tile); - tile.used = true; - } - } - - result.order.emplace_back(*layer, std::move(sortedTilesForInsertion)); - } - - return result; -} - -std::vector<Feature> Style::queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const { - std::unordered_map<std::string, std::vector<Feature>> resultsByLayer; - - if (options.layerIDs) { - std::unordered_set<std::string> sourceIDs; - for (const auto& layerID : *options.layerIDs) { - if (Layer* layer = getLayer(layerID)) { - sourceIDs.emplace(layer->baseImpl->source); - } - } - for (const auto& sourceID : sourceIDs) { - if (RenderSource* renderSource = getRenderSource(sourceID)) { - auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, options); - std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); - } - } - } else { - for (const auto& renderSource : renderSources) { - auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, options); - std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); - } - } - - std::vector<Feature> result; - - if (resultsByLayer.empty()) { - return result; - } - - // Combine all results based on the style layer order. - for (const auto& layer : renderLayers) { - if (!layer->needsRendering(zoomHistory.lastZoom)) { - continue; - } - auto it = resultsByLayer.find(layer->baseImpl.id); - if (it != resultsByLayer.end()) { - std::move(it->second.begin(), it->second.end(), std::back_inserter(result)); - } - } - - return result; -} - -void Style::setSourceTileCacheSize(size_t size) { - for (const auto& renderSource : renderSources) { - renderSource->setCacheSize(size); - } -} - -void Style::onLowMemory() { - for (const auto& renderSource : renderSources) { - renderSource->onLowMemory(); - } -} - -void Style::setObserver(style::Observer* observer_) { - observer = observer_; -} - -void Style::onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) { - observer->onGlyphsLoaded(fontStack, glyphRange); -} - -void Style::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { - lastError = error; - Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s", - glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str()); - observer->onGlyphsError(fontStack, glyphRange, error); - observer->onResourceError(error); -} - -void Style::onSourceLoaded(Source& source) { - observer->onSourceLoaded(source); - observer->onUpdate(Update::Repaint); -} - -void Style::onSourceChanged(Source& source) { - observer->onSourceChanged(source); - observer->onUpdate(Update::Repaint); -} - -void Style::onSourceError(Source& source, std::exception_ptr error) { - lastError = error; - Log::Error(Event::Style, "Failed to load source %s: %s", - source.getID().c_str(), util::toString(error).c_str()); - observer->onSourceError(source, error); - observer->onResourceError(error); -} - -void Style::onSourceDescriptionChanged(Source& source) { - observer->onSourceDescriptionChanged(source); - if (!source.baseImpl->loaded) { - source.baseImpl->loadDescription(fileSource); - } -} - -void Style::onTileChanged(RenderSource&, const OverscaledTileID&) { - observer->onUpdate(Update::Repaint); +std::vector<const Source*> Style::getSources() const { + return const_cast<const Impl&>(*impl).getSources(); } -void Style::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) { - lastError = error; - Log::Error(Event::Style, "Failed to load tile %s for source %s: %s", - util::toString(tileID).c_str(), source.baseImpl.id.c_str(), util::toString(error).c_str()); - observer->onResourceError(error); +Source* Style::getSource(const std::string& id) { + impl->mutated = true; + return impl->getSource(id); } -void Style::onSpriteLoaded() { - observer->onSpriteLoaded(); - observer->onUpdate(Update::Repaint); // For *-pattern properties. +const Source* Style::getSource(const std::string& id) const { + return impl->getSource(id); } -void Style::onSpriteError(std::exception_ptr error) { - lastError = error; - Log::Error(Event::Style, "Failed to load sprite: %s", util::toString(error).c_str()); - observer->onSpriteError(error); - observer->onResourceError(error); +void Style::addSource(std::unique_ptr<Source> source) { + impl->mutated = true; + impl->addSource(std::move(source)); } -void Style::onLayerFilterChanged(Layer& layer) { - layer.accept(QueueSourceReloadVisitor { updateBatch }); - observer->onUpdate(Update::Repaint); +std::unique_ptr<Source> Style::removeSource(const std::string& sourceID) { + impl->mutated = true; + return impl->removeSource(sourceID); } -void Style::onLayerVisibilityChanged(Layer& layer) { - layer.accept(QueueSourceReloadVisitor { updateBatch }); - observer->onUpdate(Update::RecalculateStyle); +std::vector<Layer*> Style::getLayers() { + impl->mutated = true; + return impl->getLayers(); } -void Style::onLayerPaintPropertyChanged(Layer&) { - observer->onUpdate(Update::RecalculateStyle | Update::Classes); +std::vector<const Layer*> Style::getLayers() const { + return const_cast<const Impl&>(*impl).getLayers(); } -void Style::onLayerDataDrivenPaintPropertyChanged(Layer& layer) { - layer.accept(QueueSourceReloadVisitor { updateBatch }); - observer->onUpdate(Update::RecalculateStyle | Update::Classes); +Layer* Style::getLayer(const std::string& layerID) { + impl->mutated = true; + return impl->getLayer(layerID); } -void Style::onLayerLayoutPropertyChanged(Layer& layer, const char * property) { - layer.accept(QueueSourceReloadVisitor { updateBatch }); - - // Recalculate the style for certain properties - observer->onUpdate((strcmp(property, "icon-size") == 0 || strcmp(property, "text-size") == 0) - ? Update::RecalculateStyle - : Update::Repaint); +const Layer* Style::getLayer(const std::string& layerID) const { + return impl->getLayer(layerID); } -void Style::onLightChanged(const Light&) { - observer->onUpdate(Update::Classes | Update::RecalculateStyle); +void Style::addLayer(std::unique_ptr<Layer> layer, const optional<std::string>& before) { + impl->mutated = true; + impl->addLayer(std::move(layer), before); } -void Style::dumpDebugLogs() const { - for (const auto& source : sources) { - source->baseImpl->dumpDebugLogs(); - } - - for (const auto& renderSource : renderSources) { - renderSource->dumpDebugLogs(); - } - - spriteAtlas->dumpDebugLogs(); +std::unique_ptr<Layer> Style::removeLayer(const std::string& id) { + impl->mutated = true; + return impl->removeLayer(id); } } // namespace style diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp deleted file mode 100644 index 7d235dc665..0000000000 --- a/src/mbgl/style/style.hpp +++ /dev/null @@ -1,192 +0,0 @@ -#pragma once - -#include <mbgl/style/transition_options.hpp> -#include <mbgl/style/observer.hpp> -#include <mbgl/style/source_observer.hpp> -#include <mbgl/renderer/render_source_observer.hpp> -#include <mbgl/style/layer_observer.hpp> -#include <mbgl/style/light_observer.hpp> -#include <mbgl/style/update_batch.hpp> -#include <mbgl/renderer/render_layer.hpp> -#include <mbgl/renderer/render_light.hpp> -#include <mbgl/text/glyph_atlas_observer.hpp> -#include <mbgl/sprite/sprite_atlas_observer.hpp> -#include <mbgl/map/mode.hpp> -#include <mbgl/map/zoom_history.hpp> - -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/chrono.hpp> -#include <mbgl/util/optional.hpp> -#include <mbgl/util/feature.hpp> -#include <mbgl/util/geo.hpp> - -#include <cstdint> -#include <memory> -#include <string> -#include <vector> - -namespace mbgl { - -class FileSource; -class GlyphAtlas; -class SpriteAtlas; -class LineAtlas; -class RenderData; -class TransformState; -class RenderedQueryOptions; -class Scheduler; -class RenderLayer; -class RenderSource; -class UpdateParameters; - -namespace style { - -class Layer; -class QueryParameters; - -class Style : public GlyphAtlasObserver, - public SpriteAtlasObserver, - public SourceObserver, - public RenderSourceObserver, - public LayerObserver, - public LightObserver, - public util::noncopyable { -public: - Style(Scheduler&, FileSource&, float pixelRatio); - ~Style() override; - - void setJSON(const std::string&); - - void setObserver(Observer*); - - bool isLoaded() const; - - void update(const UpdateParameters&); - - bool hasTransitions() const; - - std::exception_ptr getLastError() const { - return lastError; - } - - std::vector<const Source*> getSources() const; - std::vector<Source*> getSources(); - Source* getSource(const std::string& id) const; - void addSource(std::unique_ptr<Source>); - std::unique_ptr<Source> removeSource(const std::string& sourceID); - - std::vector<const Layer*> getLayers() const; - std::vector<Layer*> getLayers(); - Layer* getLayer(const std::string& id) const; - Layer* addLayer(std::unique_ptr<Layer>, - optional<std::string> beforeLayerID = {}); - std::unique_ptr<Layer> removeLayer(const std::string& layerID); - - // Should be moved to Impl eventually - std::vector<const RenderLayer*> getRenderLayers() const; - std::vector<RenderLayer*> getRenderLayers(); - RenderLayer* getRenderLayer(const std::string& id) const; - - std::string getName() const; - LatLng getDefaultLatLng() const; - double getDefaultZoom() const; - double getDefaultBearing() const; - double getDefaultPitch() const; - - bool addClass(const std::string&); - bool removeClass(const std::string&); - void setClasses(const std::vector<std::string>&); - - TransitionOptions getTransitionOptions() const; - void setTransitionOptions(const TransitionOptions&); - - bool hasClass(const std::string&) const; - std::vector<std::string> getClasses() const; - - void setLight(std::unique_ptr<Light>); - Light* getLight() const; - RenderLight* getRenderLight() const; - - RenderData getRenderData(MapDebugOptions, float angle) const; - - std::vector<Feature> queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const; - - void setSourceTileCacheSize(size_t); - void onLowMemory(); - - void dumpDebugLogs() const; - - Scheduler& scheduler; - FileSource& fileSource; - std::unique_ptr<GlyphAtlas> glyphAtlas; - std::unique_ptr<SpriteAtlas> spriteAtlas; - std::unique_ptr<LineAtlas> lineAtlas; - - RenderSource* getRenderSource(const std::string& id) const; - -private: - std::vector<std::unique_ptr<Source>> sources; - std::vector<std::unique_ptr<RenderSource>> renderSources; - - std::vector<std::unique_ptr<Layer>> layers; - std::vector<std::unique_ptr<RenderLayer>> renderLayers; - std::vector<std::string> classes; - TransitionOptions transitionOptions; - - std::unique_ptr<Light> light; - std::unique_ptr<RenderLight> renderLight; - - // Defaults - std::string name; - LatLng defaultLatLng; - double defaultZoom = 0; - double defaultBearing = 0; - double defaultPitch = 0; - - std::vector<std::unique_ptr<Layer>>::const_iterator findLayer(const std::string& layerID) const; - std::vector<std::unique_ptr<RenderLayer>>::const_iterator findRenderLayer(const std::string&) const; - - // GlyphStoreObserver implementation. - void onGlyphsLoaded(const FontStack&, const GlyphRange&) override; - void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override; - - // SpriteStoreObserver implementation. - void onSpriteLoaded() override; - void onSpriteError(std::exception_ptr) override; - - // SourceObserver implementation. - void onSourceLoaded(Source&) override; - void onSourceChanged(Source&) override; - void onSourceError(Source&, std::exception_ptr) override; - void onSourceDescriptionChanged(Source&) override; - void onTileChanged(RenderSource&, const OverscaledTileID&) override; - void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; - - // LayerObserver implementation. - void onLayerFilterChanged(Layer&) override; - void onLayerVisibilityChanged(Layer&) override; - void onLayerPaintPropertyChanged(Layer&) override; - void onLayerDataDrivenPaintPropertyChanged(Layer&) override; - void onLayerLayoutPropertyChanged(Layer&, const char *) override; - - // LightObserver implementation. - void onLightChanged(const Light&) override; - - Observer nullObserver; - Observer* observer = &nullObserver; - - std::exception_ptr lastError; - - UpdateBatch updateBatch; - ZoomHistory zoomHistory; - - void removeRenderLayer(const std::string& layerID); - -public: - bool loaded = false; -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp new file mode 100644 index 0000000000..37907d3f60 --- /dev/null +++ b/src/mbgl/style/style_impl.cpp @@ -0,0 +1,364 @@ +#include <mbgl/style/style_impl.hpp> +#include <mbgl/style/observer.hpp> +#include <mbgl/style/source_impl.hpp> +#include <mbgl/style/layers/symbol_layer.hpp> +#include <mbgl/style/layers/custom_layer.hpp> +#include <mbgl/style/layers/background_layer.hpp> +#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/style/layers/raster_layer.hpp> +#include <mbgl/style/layer_impl.hpp> +#include <mbgl/style/parser.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/sprite/sprite_loader.hpp> +#include <mbgl/util/exception.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> + +namespace mbgl { +namespace style { + +static Observer nullObserver; + +Style::Impl::Impl(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio) + : scheduler(scheduler_), + fileSource(fileSource_), + spriteLoader(std::make_unique<SpriteLoader>(pixelRatio)), + light(std::make_unique<Light>()), + observer(&nullObserver) { + spriteLoader->setObserver(this); + light->setObserver(this); +} + +Style::Impl::~Impl() = default; + +void Style::Impl::loadJSON(const std::string& json_) { + lastError = nullptr; + observer->onStyleLoading(); + + url.clear(); + parse(json_); +} + +void Style::Impl::loadURL(const std::string& url_) { + lastError = nullptr; + observer->onStyleLoading(); + + loaded = false; + url = url_; + + styleRequest = fileSource.request(Resource::style(url), [this](Response res) { + // Once we get a fresh style, or the style is mutated, stop revalidating. + if (res.isFresh() || mutated) { + styleRequest.reset(); + } + + // Don't allow a loaded, mutated style to be overwritten with a new version. + if (mutated && loaded) { + return; + } + + if (res.error) { + const std::string message = "loading style failed: " + res.error->message; + Log::Error(Event::Setup, message.c_str()); + observer->onStyleError(std::make_exception_ptr(util::StyleLoadException(message))); + observer->onResourceError(std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified || res.noContent) { + return; + } else { + parse(*res.data); + } + }); +} + +void Style::Impl::parse(const std::string& json_) { + Parser parser; + + if (auto error = parser.parse(json_)) { + std::string message = "Failed to parse style: " + util::toString(error); + Log::Error(Event::ParseStyle, message.c_str()); + observer->onStyleError(std::make_exception_ptr(util::StyleParseException(message))); + observer->onResourceError(error); + return; + } + + mutated = false; + loaded = false; + json = json_; + + sources.clear(); + layers.clear(); + images.clear(); + + transitionOptions = {}; + transitionOptions.duration = util::DEFAULT_TRANSITION_DURATION; + + for (auto& source : parser.sources) { + addSource(std::move(source)); + } + + for (auto& layer : parser.layers) { + addLayer(std::move(layer)); + } + + name = parser.name; + defaultCamera.center = parser.latLng; + defaultCamera.zoom = parser.zoom; + defaultCamera.angle = parser.bearing; + defaultCamera.pitch = parser.pitch; + + setLight(std::make_unique<Light>(parser.light)); + + spriteLoaded = false; + spriteLoader->load(parser.spriteURL, scheduler, fileSource); + glyphURL = parser.glyphURL; + + loaded = true; + observer->onStyleLoaded(); +} + +std::string Style::Impl::getJSON() const { + return json; +} + +std::string Style::Impl::getURL() const { + return url; +} + +void Style::Impl::setTransitionOptions(const TransitionOptions& options) { + transitionOptions = options; +} + +TransitionOptions Style::Impl::getTransitionOptions() const { + return transitionOptions; +} + +void Style::Impl::addSource(std::unique_ptr<Source> source) { + if (sources.get(source->getID())) { + std::string msg = "Source " + source->getID() + " already exists"; + throw std::runtime_error(msg.c_str()); + } + + source->setObserver(this); + source->loadDescription(fileSource); + + sources.add(std::move(source)); +} + +struct SourceIdUsageEvaluator { + const std::string& sourceId; + + bool operator()(BackgroundLayer&) { return false; } + bool operator()(CustomLayer&) { return false; } + + template <class LayerType> + bool operator()(LayerType& layer) { + return layer.getSourceID() == sourceId; + } +}; + +std::unique_ptr<Source> Style::Impl::removeSource(const std::string& id) { + // Check if source is in use + SourceIdUsageEvaluator sourceIdEvaluator {id}; + auto layerIt = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { + return layer->accept(sourceIdEvaluator); + }); + + if (layerIt != layers.end()) { + Log::Warning(Event::General, "Source '%s' is in use, cannot remove", id.c_str()); + return nullptr; + } + + std::unique_ptr<Source> source = sources.remove(id); + + if (source) { + source->setObserver(nullptr); + } + + return source; +} + +std::vector<Layer*> Style::Impl::getLayers() { + return layers.getWrappers(); +} + +std::vector<const Layer*> Style::Impl::getLayers() const { + auto wrappers = layers.getWrappers(); + return std::vector<const Layer*>(wrappers.begin(), wrappers.end()); +} + +Layer* Style::Impl::getLayer(const std::string& id) const { + return layers.get(id); +} + +Layer* Style::Impl::addLayer(std::unique_ptr<Layer> layer, optional<std::string> before) { + // TODO: verify source + + if (layers.get(layer->getID())) { + throw std::runtime_error(std::string{"Layer "} + layer->getID() + " already exists"); + } + + layer->setObserver(this); + observer->onUpdate(); + + return layers.add(std::move(layer), before); +} + +std::unique_ptr<Layer> Style::Impl::removeLayer(const std::string& id) { + std::unique_ptr<Layer> layer = layers.remove(id); + + if (layer) { + layer->setObserver(nullptr); + observer->onUpdate(); + } + + return layer; +} + +void Style::Impl::setLight(std::unique_ptr<Light> light_) { + light = std::move(light_); + light->setObserver(this); + onLightChanged(*light); +} + +Light* Style::Impl::getLight() const { + return light.get(); +} + +std::string Style::Impl::getName() const { + return name; +} + +CameraOptions Style::Impl::getDefaultCamera() const { + return defaultCamera; +} + +std::vector<Source*> Style::Impl::getSources() { + return sources.getWrappers(); +} + +std::vector<const Source*> Style::Impl::getSources() const { + auto wrappers = sources.getWrappers(); + return std::vector<const Source*>(wrappers.begin(), wrappers.end()); +} + +Source* Style::Impl::getSource(const std::string& id) const { + return sources.get(id); +} + +bool Style::Impl::isLoaded() const { + if (!loaded) { + return false; + } + + if (!spriteLoaded) { + return false; + } + + for (const auto& source: sources) { + if (!source->loaded) { + return false; + } + } + + return true; +} + +void Style::Impl::addImage(std::unique_ptr<style::Image> image) { + images.remove(image->getID()); // We permit using addImage to update. + images.add(std::move(image)); +} + +void Style::Impl::removeImage(const std::string& id) { + images.remove(id); +} + +const style::Image* Style::Impl::getImage(const std::string& id) const { + return images.get(id); +} + +void Style::Impl::setObserver(style::Observer* observer_) { + observer = observer_; +} + +void Style::Impl::onSourceLoaded(Source& source) { + sources.update(source); + observer->onSourceLoaded(source); + observer->onUpdate(); +} + +void Style::Impl::onSourceChanged(Source& source) { + sources.update(source); + observer->onSourceChanged(source); + observer->onUpdate(); +} + +void Style::Impl::onSourceError(Source& source, std::exception_ptr error) { + lastError = error; + Log::Error(Event::Style, "Failed to load source %s: %s", + source.getID().c_str(), util::toString(error).c_str()); + observer->onSourceError(source, error); + observer->onResourceError(error); +} + +void Style::Impl::onSourceDescriptionChanged(Source& source) { + sources.update(source); + observer->onSourceDescriptionChanged(source); + if (!source.loaded) { + source.loadDescription(fileSource); + } +} + +void Style::Impl::onSpriteLoaded(std::vector<std::unique_ptr<Image>>&& images_) { + for (auto& image : images_) { + addImage(std::move(image)); + } + spriteLoaded = true; + observer->onUpdate(); // For *-pattern properties. +} + +void Style::Impl::onSpriteError(std::exception_ptr error) { + lastError = error; + Log::Error(Event::Style, "Failed to load sprite: %s", util::toString(error).c_str()); + observer->onResourceError(error); +} + +void Style::Impl::onLayerChanged(Layer& layer) { + layers.update(layer); + observer->onUpdate(); +} + +void Style::Impl::onLightChanged(const Light&) { + observer->onUpdate(); +} + +void Style::Impl::dumpDebugLogs() const { + Log::Info(Event::General, "styleURL: %s", url.c_str()); + for (const auto& source : sources) { + source->dumpDebugLogs(); + } +} + +const std::string& Style::Impl::getGlyphURL() const { + return glyphURL; +} + +Immutable<std::vector<Immutable<Image::Impl>>> Style::Impl::getImageImpls() const { + return images.getImpls(); +} + +Immutable<std::vector<Immutable<Source::Impl>>> Style::Impl::getSourceImpls() const { + return sources.getImpls(); +} + +Immutable<std::vector<Immutable<Layer::Impl>>> Style::Impl::getLayerImpls() const { + return layers.getImpls(); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp new file mode 100644 index 0000000000..3dc222bfad --- /dev/null +++ b/src/mbgl/style/style_impl.hpp @@ -0,0 +1,144 @@ +#pragma once + +#include <mbgl/style/style.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/style/observer.hpp> +#include <mbgl/style/source_observer.hpp> +#include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/light_observer.hpp> +#include <mbgl/sprite/sprite_loader_observer.hpp> +#include <mbgl/style/image.hpp> +#include <mbgl/style/source.hpp> +#include <mbgl/style/layer.hpp> +#include <mbgl/style/collection.hpp> + +#include <mbgl/map/camera.hpp> + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/geo.hpp> + +#include <memory> +#include <string> +#include <vector> +#include <unordered_map> + +namespace mbgl { + +class Scheduler; +class FileSource; +class AsyncRequest; +class SpriteLoader; + +namespace style { + +class Style::Impl : public SpriteLoaderObserver, + public SourceObserver, + public LayerObserver, + public LightObserver, + public util::noncopyable { +public: + Impl(Scheduler&, FileSource&, float pixelRatio); + ~Impl() override; + + void loadJSON(const std::string&); + void loadURL(const std::string&); + + std::string getJSON() const; + std::string getURL() const; + + void setObserver(Observer*); + + bool isLoaded() const; + + std::exception_ptr getLastError() const { + return lastError; + } + + std::vector< Source*> getSources(); + std::vector<const Source*> getSources() const; + Source* getSource(const std::string& id) const; + + void addSource(std::unique_ptr<Source>); + std::unique_ptr<Source> removeSource(const std::string& sourceID); + + std::vector< Layer*> getLayers(); + std::vector<const Layer*> getLayers() const; + Layer* getLayer(const std::string& id) const; + + Layer* addLayer(std::unique_ptr<Layer>, + optional<std::string> beforeLayerID = {}); + std::unique_ptr<Layer> removeLayer(const std::string& layerID); + + std::string getName() const; + CameraOptions getDefaultCamera() const; + + TransitionOptions getTransitionOptions() const; + void setTransitionOptions(const TransitionOptions&); + + void setLight(std::unique_ptr<Light>); + Light* getLight() const; + + const style::Image* getImage(const std::string&) const; + void addImage(std::unique_ptr<style::Image>); + void removeImage(const std::string&); + + const std::string& getGlyphURL() const; + + Immutable<std::vector<Immutable<Image::Impl>>> getImageImpls() const; + Immutable<std::vector<Immutable<Source::Impl>>> getSourceImpls() const; + Immutable<std::vector<Immutable<Layer::Impl>>> getLayerImpls() const; + + void dumpDebugLogs() const; + + bool mutated = false; + bool loaded = false; + bool spriteLoaded = false; + +private: + void parse(const std::string&); + + Scheduler& scheduler; + FileSource& fileSource; + + std::string url; + std::string json; + + std::unique_ptr<AsyncRequest> styleRequest; + std::unique_ptr<SpriteLoader> spriteLoader; + + std::string glyphURL; + Collection<style::Image> images; + Collection<Source> sources; + Collection<Layer> layers; + TransitionOptions transitionOptions; + std::unique_ptr<Light> light; + + // Defaults + std::string name; + CameraOptions defaultCamera; + + // SpriteLoaderObserver implementation. + void onSpriteLoaded(std::vector<std::unique_ptr<Image>>&&) override; + void onSpriteError(std::exception_ptr) override; + + // SourceObserver implementation. + void onSourceLoaded(Source&) override; + void onSourceChanged(Source&) override; + void onSourceError(Source&, std::exception_ptr) override; + void onSourceDescriptionChanged(Source&) override; + + // LayerObserver implementation. + void onLayerChanged(Layer&) override; + + // LightObserver implementation. + void onLightChanged(const Light&) override; + + Observer nullObserver; + Observer* observer = &nullObserver; + + std::exception_ptr lastError; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/tile_source_impl.cpp b/src/mbgl/style/tile_source_impl.cpp deleted file mode 100644 index d2ce3def9f..0000000000 --- a/src/mbgl/style/tile_source_impl.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include <mbgl/style/tile_source_impl.hpp> -#include <mbgl/style/source_observer.hpp> -#include <mbgl/style/conversion/json.hpp> -#include <mbgl/style/conversion/tileset.hpp> -#include <mbgl/util/mapbox.hpp> -#include <mbgl/storage/file_source.hpp> - -namespace mbgl { -namespace style { - -TileSourceImpl::TileSourceImpl(SourceType type_, std::string id_, Source& base_, - variant<std::string, Tileset> urlOrTileset_, - uint16_t tileSize_) - : Impl(type_, std::move(id_), base_), - urlOrTileset(std::move(urlOrTileset_)), - tileSize(tileSize_) { -} - -TileSourceImpl::~TileSourceImpl() = default; - -void TileSourceImpl::loadDescription(FileSource& fileSource) { - if (urlOrTileset.is<Tileset>()) { - tileset = urlOrTileset.get<Tileset>(); - loaded = true; - return; - } - - if (req) { - return; - } - - const std::string& url = urlOrTileset.get<std::string>(); - req = fileSource.request(Resource::source(url), [this, url](Response res) { - if (res.error) { - observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified) { - return; - } else if (res.noContent) { - observer->onSourceError(base, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON"))); - } else { - conversion::Error error; - optional<Tileset> newTileset = conversion::convertJSON<Tileset>(*res.data, error); - if (!newTileset) { - observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(error.message))); - return; - } - - util::mapbox::canonicalizeTileset(*newTileset, url, type, tileSize); - bool attributionChanged = tileset.attribution != (*newTileset).attribution; - - tileset = *newTileset; - loaded = true; - - observer->onSourceLoaded(base); - if (attributionChanged) { - observer->onSourceChanged(base); - } - } - }); -} - -optional<Tileset> TileSourceImpl::getTileset() const { - if (loaded) { - return tileset; - } - return {}; -} - -optional<std::string> TileSourceImpl::getAttribution() const { - if (loaded && !tileset.attribution.empty()) { - return tileset.attribution; - } else { - return {}; - } -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/tile_source_impl.hpp b/src/mbgl/style/tile_source_impl.hpp deleted file mode 100644 index 0e5a53add7..0000000000 --- a/src/mbgl/style/tile_source_impl.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include <mbgl/style/source_impl.hpp> -#include <mbgl/util/tileset.hpp> -#include <mbgl/util/variant.hpp> -#include <mbgl/util/optional.hpp> - -namespace mbgl { - -class AsyncRequest; - -namespace style { - -/* - Shared implementation for VectorSource and RasterSource. Should eventually - be refactored to use composition rather than inheritance. -*/ -class TileSourceImpl : public Source::Impl { -public: - TileSourceImpl(SourceType, std::string id, Source&, - variant<std::string, Tileset> urlOrTileset, - uint16_t tileSize); - ~TileSourceImpl() override; - - void loadDescription(FileSource&) final; - - uint16_t getTileSize() const { - return tileSize; - } - - const variant<std::string, Tileset>& getURLOrTileset() const { - return urlOrTileset; - } - - optional<std::string> getAttribution() const override; - optional<Tileset> getTileset() const; - -protected: - const variant<std::string, Tileset> urlOrTileset; - const uint16_t tileSize; - - Tileset tileset; - std::unique_ptr<AsyncRequest> req; -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp index b37e73ffb1..0a1781e01b 100644 --- a/src/mbgl/style/types.cpp +++ b/src/mbgl/style/types.cpp @@ -11,6 +11,7 @@ MBGL_DEFINE_ENUM(SourceType, { { SourceType::GeoJSON, "geojson" }, { SourceType::Video, "video" }, { SourceType::Annotations, "annotations" }, + { SourceType::Image, "image" }, }); MBGL_DEFINE_ENUM(VisibilityType, { @@ -52,16 +53,16 @@ MBGL_DEFINE_ENUM(SymbolPlacementType, { { SymbolPlacementType::Line, "line" }, }); -MBGL_DEFINE_ENUM(TextAnchorType, { - { TextAnchorType::Center, "center" }, - { TextAnchorType::Left, "left" }, - { TextAnchorType::Right, "right" }, - { TextAnchorType::Top, "top" }, - { TextAnchorType::Bottom, "bottom" }, - { TextAnchorType::TopLeft, "top-left" }, - { TextAnchorType::TopRight, "top-right" }, - { TextAnchorType::BottomLeft, "bottom-left" }, - { TextAnchorType::BottomRight, "bottom-right" } +MBGL_DEFINE_ENUM(SymbolAnchorType, { + { SymbolAnchorType::Center, "center" }, + { SymbolAnchorType::Left, "left" }, + { SymbolAnchorType::Right, "right" }, + { SymbolAnchorType::Top, "top" }, + { SymbolAnchorType::Bottom, "bottom" }, + { SymbolAnchorType::TopLeft, "top-left" }, + { SymbolAnchorType::TopRight, "top-right" }, + { SymbolAnchorType::BottomLeft, "bottom-left" }, + { SymbolAnchorType::BottomRight, "bottom-right" } }); MBGL_DEFINE_ENUM(TextJustifyType, { diff --git a/src/mbgl/style/update_batch.hpp b/src/mbgl/style/update_batch.hpp deleted file mode 100644 index 562df52afa..0000000000 --- a/src/mbgl/style/update_batch.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include <unordered_set> -#include <string> - -namespace mbgl { -namespace style { - -class UpdateBatch { -public: - std::unordered_set<std::string> sourceIDs; -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp index 885ba5c426..3eb08da8d1 100644 --- a/src/mbgl/text/collision_feature.cpp +++ b/src/mbgl/text/collision_feature.cpp @@ -42,14 +42,18 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line, bboxifyLabel(line, anchorPoint, anchor.segment, length, height); } } else { - boxes.emplace_back(anchor.point, x1, y1, x2, y2, std::numeric_limits<float>::infinity()); + boxes.emplace_back(anchor.point, Point<float>{ 0, 0 }, x1, y1, x2, y2, std::numeric_limits<float>::infinity()); } } void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint, const int segment, const float labelLength, const float boxSize) { const float step = boxSize / 2; - const unsigned int nBoxes = std::floor(labelLength / step); + const int nBoxes = std::floor(labelLength / step); + // We calculate line collision boxes out to 300% of what would normally be our + // max size, to allow collision detection to work on labels that expand as + // they move into the distance + const int nPitchPaddingBoxes = std::floor(nBoxes / 2); // offset the center of the first box by half a box so that the edge of the // box is at the edge of the label. @@ -58,24 +62,46 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo GeometryCoordinate &p = anchorPoint; int index = segment + 1; float anchorDistance = firstBoxOffset; + const float labelStartDistance = -labelLength / 2; + const float paddingStartDistance = labelStartDistance - labelLength / 8; // move backwards along the line to the first segment the label appears on do { index--; - // there isn't enough room for the label after the beginning of the line - // checkMaxAngle should have already caught this - if (index < 0) return; + if (index < 0) { + if (anchorDistance > labelStartDistance) { + // there isn't enough room for the label after the beginning of the line + // checkMaxAngle should have already caught this + return; + } else { + // The line doesn't extend far enough back for all of our padding, + // but we got far enough to show the label under most conditions. + index = 0; + break; + } + } anchorDistance -= util::dist<float>(line[index], p); p = line[index]; - } while (anchorDistance > -labelLength / 2); + } while (anchorDistance > paddingStartDistance); - float segmentLength = util::dist<float>(line[index], line[index + 1]); + auto segmentLength = util::dist<float>(line[index], line[index + 1]); - for (unsigned int i = 0; i < nBoxes; i++) { + for (int i = -nPitchPaddingBoxes; i < nBoxes + nPitchPaddingBoxes; i++) { // the distance the box will be from the anchor - const float boxDistanceToAnchor = -labelLength / 2 + i * step; + const float boxOffset = i * step; + float boxDistanceToAnchor = labelStartDistance + boxOffset; + + // make the distance between pitch padding boxes bigger + if (boxOffset < 0) boxDistanceToAnchor += boxOffset; + if (boxOffset > labelLength) boxDistanceToAnchor += boxOffset - labelLength; + + if (boxDistanceToAnchor < anchorDistance) { + // The line doesn't extend far enough back for this box, skip it + // (This could allow for line collisions on distant tiles) + continue; + } // the box is not on the current segment. Move to the next segment. while (anchorDistance + segmentLength < boxDistanceToAnchor) { @@ -99,11 +125,46 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo p0.y + segmentBoxDistance / segmentLength * (p1.y - p0.y) }; + // Distance from label anchor point to inner (towards center) edge of this box + // The tricky thing here is that box positioning doesn't change with scale, + // but box size does change with scale. + // Technically, distanceToInnerEdge should be: + // Math.max(Math.abs(boxDistanceToAnchor - firstBoxOffset) - (step / scale), 0); + // But using that formula would make solving for maxScale more difficult, so we + // approximate with scale=2. + // This makes our calculation spot-on at scale=2, and on the conservative side for + // lower scales const float distanceToInnerEdge = std::max(std::fabs(boxDistanceToAnchor - firstBoxOffset) - step / 2, 0.0f); - const float maxScale = labelLength / 2 / distanceToInnerEdge; + float maxScale = util::division(labelLength / 2, distanceToInnerEdge, std::numeric_limits<float>::infinity()); + + // The box maxScale calculations are designed to be conservative on collisions in the scale range + // [1,2]. At scale=1, each box has 50% overlap, and at scale=2, the boxes are lined up edge + // to edge (beyond scale 2, gaps start to appear, which could potentially allow missed collisions). + // We add "pitch padding" boxes to the left and right to handle effective underzooming + // (scale < 1) when labels are in the distance. The overlap approximation could cause us to use + // these boxes when the scale is greater than 1, but we prevent that because we know + // they're only necessary for scales less than one. + // This preserves the pre-pitch-padding behavior for unpitched maps. + if (i < 0 || i >= nBoxes) { + maxScale = std::min(maxScale, 0.99f); + } - boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale); + boxes.emplace_back(boxAnchor, boxAnchor - convertPoint<float>(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale); } } +float CollisionBox::adjustedMaxScale(const std::array<float, 4>& rotationMatrix, const float yStretch) const { + // When the map is pitched the distance covered by a line changes. + // Adjust the max scale by (approximatePitchedLength / approximateRegularLength) + // to compensate for this. + const Point<float> rotatedOffset = util::matrixMultiply(rotationMatrix, offset); + const float xSqr = rotatedOffset.x * rotatedOffset.x; + const float ySqr = rotatedOffset.y * rotatedOffset.y; + const float yStretchSqr = ySqr * yStretch * yStretch; + const float adjustmentFactor = xSqr + ySqr != 0 ? + std::sqrt((xSqr + yStretchSqr) / (xSqr + ySqr)) : + 1.0f; + return maxScale * adjustmentFactor; +} + } // namespace mbgl diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp index 006a47eb74..3b6e461a26 100644 --- a/src/mbgl/text/collision_feature.hpp +++ b/src/mbgl/text/collision_feature.hpp @@ -11,11 +11,16 @@ namespace mbgl { class CollisionBox { public: - CollisionBox(Point<float> _anchor, float _x1, float _y1, float _x2, float _y2, float _maxScale) : - anchor(std::move(_anchor)), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {} + CollisionBox(Point<float> _anchor, Point<float> _offset, float _x1, float _y1, float _x2, float _y2, float _maxScale) : + anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {} + + float adjustedMaxScale(const std::array<float, 4>& rotationMatrix, const float yStretch) const; // the box is centered around the anchor point Point<float> anchor; + + // the offset of the box from the label's anchor point + Point<float> offset; // distances to the edges from the anchor float x1; @@ -34,7 +39,7 @@ public: class CollisionFeature { public: enum class AlignmentType : bool { - Straight = 0, + Straight = false, Curved }; diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp index 368750c89f..cc9b602f08 100644 --- a/src/mbgl/text/collision_tile.cpp +++ b/src/mbgl/text/collision_tile.cpp @@ -20,27 +20,39 @@ CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_ rotationMatrix = { { angle_cos, -angle_sin, angle_sin, angle_cos } }; reverseRotationMatrix = { { angle_cos, angle_sin, -angle_sin, angle_cos } }; - // Stretch boxes in y direction to account for the map tilt. - const float _yStretch = 1.0f / std::cos(config.pitch); - - // The amount the map is squished depends on the y position. - // Sort of account for this by making all boxes a bit bigger. - yStretch = std::pow(_yStretch, 1.3f); + perspectiveRatio = + 1.0f + + 0.5f * (util::division(config.cameraToTileDistance, config.cameraToCenterDistance, 1.0f) - + 1.0f); + + minScale /= perspectiveRatio; + maxScale /= perspectiveRatio; + + // We can only approximate here based on the y position of the tile + // The shaders calculate a more accurate "incidence_stretch" + // at render time to calculate an effective scale for collision + // purposes, but we still want to use the yStretch approximation + // here because we can't adjust the aspect ratio of the collision + // boxes at render time. + yStretch = util::max( + 1.0f, util::division(config.cameraToTileDistance, + config.cameraToCenterDistance * std::cos(config.pitch), 1.0f)); } - -float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, const Point<float>& blockingAnchor, const CollisionBox& blocking) { +float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, const float boxMaxScale, const Point<float>& blockingAnchor, const CollisionBox& blocking) { float minPlacementScale = minScale; // Find the lowest scale at which the two boxes can fit side by side without overlapping. // Original algorithm: - float s1 = (blocking.x1 - box.x2) / (anchor.x - blockingAnchor.x); // scale at which new box is to the left of old box - float s2 = (blocking.x2 - box.x1) / (anchor.x - blockingAnchor.x); // scale at which new box is to the right of old box - float s3 = (blocking.y1 - box.y2) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the top of old box - float s4 = (blocking.y2 - box.y1) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the bottom of old box - if (std::isnan(s1) || std::isnan(s2)) s1 = s2 = 1; - if (std::isnan(s3) || std::isnan(s4)) s3 = s4 = 1; + const float s1 = util::division(blocking.x1 - box.x2, anchor.x - blockingAnchor.x, + 1.0f); // scale at which new box is to the left of old box + const float s2 = util::division(blocking.x2 - box.x1, anchor.x - blockingAnchor.x, + 1.0f); // scale at which new box is to the right of old box + const float s3 = util::division((blocking.y1 - box.y2) * yStretch, anchor.y - blockingAnchor.y, + 1.0f); // scale at which new box is to the top of old box + const float s4 = util::division((blocking.y2 - box.y1) * yStretch, anchor.y - blockingAnchor.y, + 1.0f); // scale at which new box is to the bottom of old box float collisionFreeScale = util::min(util::max(s1, s2), util::max(s3, s4)); @@ -50,10 +62,10 @@ float CollisionTile::findPlacementScale(const Point<float>& anchor, const Collis collisionFreeScale = blocking.maxScale; } - if (collisionFreeScale > box.maxScale) { + if (collisionFreeScale > boxMaxScale) { // If the box can only be shown after it is visible, then the box can never be shown. // But the label can be shown after this box is not visible. - collisionFreeScale = box.maxScale; + collisionFreeScale = boxMaxScale; } if (collisionFreeScale > minPlacementScale && @@ -72,13 +84,13 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve static const float infinity = std::numeric_limits<float>::infinity(); static const std::array<CollisionBox, 4> edges {{ // left - CollisionBox(Point<float>(0, 0), 0, -infinity, 0, infinity, infinity), + CollisionBox(Point<float>(0, 0), { 0, 0 }, 0, -infinity, 0, infinity, infinity), // right - CollisionBox(Point<float>(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity), + CollisionBox(Point<float>(util::EXTENT, 0), { 0, 0 }, 0, -infinity, 0, infinity, infinity), // top - CollisionBox(Point<float>(0, 0), -infinity, 0, infinity, 0, infinity), + CollisionBox(Point<float>(0, 0), { 0, 0 }, -infinity, 0, infinity, 0, infinity), // bottom - CollisionBox(Point<float>(0, util::EXTENT), -infinity, 0, infinity, 0, infinity) + CollisionBox(Point<float>(0, util::EXTENT), { 0, 0 }, -infinity, 0, infinity, 0, infinity) }}; float minPlacementScale = minScale; @@ -86,12 +98,14 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve for (auto& box : feature.boxes) { const auto anchor = util::matrixMultiply(rotationMatrix, box.anchor); + const float boxMaxScale = box.adjustedMaxScale(rotationMatrix, yStretch); + if (!allowOverlap) { for (auto it = tree.qbegin(bgi::intersects(getTreeBox(anchor, box))); it != tree.qend(); ++it) { const CollisionBox& blocking = std::get<1>(*it); Point<float> blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor); - minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, blockingAnchor, blocking)); + minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, boxMaxScale, blockingAnchor, blocking)); if (minPlacementScale >= maxScale) return minPlacementScale; } } @@ -102,14 +116,15 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y2 }); const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y2 }); CollisionBox rotatedBox(box.anchor, + box.offset, util::min(rtl.x, rtr.x, rbl.x, rbr.x), util::min(rtl.y, rtr.y, rbl.y, rbr.y), util::max(rtl.x, rtr.x, rbl.x, rbr.x), util::max(rtl.y, rtr.y, rbl.y, rbr.y), - box.maxScale); + boxMaxScale); for (auto& blocking : edges) { - minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, blocking.anchor, blocking)); + minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, boxMaxScale, blocking.anchor, blocking)); if (minPlacementScale >= maxScale) return minPlacementScale; } } @@ -126,7 +141,9 @@ void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementS if (minPlacementScale < maxScale) { std::vector<CollisionTreeBox> treeBoxes; for (auto& box : feature.boxes) { - treeBoxes.emplace_back(getTreeBox(util::matrixMultiply(rotationMatrix, box.anchor), box), box, feature.indexedFeature); + CollisionBox adjustedBox = box; + box.maxScale = box.adjustedMaxScale(rotationMatrix, yStretch); + treeBoxes.emplace_back(getTreeBox(util::matrixMultiply(rotationMatrix, box.anchor), box), std::move(adjustedBox), feature.indexedFeature); } if (ignorePlacement) { ignoredTree.insert(treeBoxes.begin(), treeBoxes.end()); @@ -157,13 +174,21 @@ void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementS Box CollisionTile::getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale) { assert(box.x1 <= box.x2 && box.y1 <= box.y2); return Box{ + // When the 'perspectiveRatio' is high, we're effectively underzooming + // the tile because it's in the distance. + // In order to detect collisions that only happen while underzoomed, + // we have to query a larger portion of the grid. + // This extra work is offset by having a lower 'maxScale' bound + // Note that this adjustment ONLY affects the bounding boxes + // in the grid. It doesn't affect the boxes used for the + // minPlacementScale calculations. CollisionPoint{ - anchor.x + box.x1 / scale, - anchor.y + box.y1 / scale * yStretch + anchor.x + box.x1 / scale * perspectiveRatio, + anchor.y + box.y1 / scale * yStretch * perspectiveRatio, }, CollisionPoint{ - anchor.x + box.x2 / scale, - anchor.y + box.y2 / scale * yStretch + anchor.x + box.x2 / scale * perspectiveRatio, + anchor.y + box.y2 / scale * yStretch * perspectiveRatio } }; } @@ -190,23 +215,30 @@ std::vector<IndexedSubfeature> CollisionTile::queryRenderedSymbols(const Geometr return seenFeatures.find(feature.index) == seenFeatures.end(); }; + // "perspectiveRatio" is a tile-based approximation of how much larger symbols will + // be in the distance. It won't line up exactly with the actually rendered symbols + // Being exact would require running the collision detection logic in symbol_sdf.vertex + // in the CPU + const float perspectiveScale = scale / perspectiveRatio; + // Account for the rounding done when updating symbol shader variables. - const float roundedScale = std::pow(2.0f, std::ceil(util::log2(scale) * 10.0f) / 10.0f); + const float roundedScale = std::pow(2.0f, std::ceil(util::log2(perspectiveScale) * 10.0f) / 10.0f); // Check if feature is rendered (collision free) at current scale. auto visibleAtScale = [&] (const CollisionTreeBox& treeBox) -> bool { const CollisionBox& box = std::get<1>(treeBox); - return roundedScale >= box.placementScale && roundedScale <= box.maxScale; + return roundedScale >= box.placementScale && roundedScale <= box.adjustedMaxScale(rotationMatrix, yStretch); }; // Check if query polygon intersects with the feature box at current scale. auto intersectsAtScale = [&] (const CollisionTreeBox& treeBox) -> bool { const CollisionBox& collisionBox = std::get<1>(treeBox); const auto anchor = util::matrixMultiply(rotationMatrix, collisionBox.anchor); - const int16_t x1 = anchor.x + collisionBox.x1 / scale; - const int16_t y1 = anchor.y + collisionBox.y1 / scale * yStretch; - const int16_t x2 = anchor.x + collisionBox.x2 / scale; - const int16_t y2 = anchor.y + collisionBox.y2 / scale * yStretch; + + const int16_t x1 = anchor.x + (collisionBox.x1 / perspectiveScale); + const int16_t y1 = anchor.y + (collisionBox.y1 / perspectiveScale) * yStretch; + const int16_t x2 = anchor.x + (collisionBox.x2 / perspectiveScale); + const int16_t y2 = anchor.y + (collisionBox.y2 / perspectiveScale) * yStretch; auto bbox = GeometryCoordinates { { x1, y1 }, { x2, y1 }, { x2, y2 }, { x1, y2 } }; diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp index 4508e13a4b..9868266aa2 100644 --- a/src/mbgl/text/collision_tile.hpp +++ b/src/mbgl/text/collision_tile.hpp @@ -31,10 +31,10 @@ namespace mbgl { namespace bg = boost::geometry; namespace bgm = bg::model; namespace bgi = bg::index; -typedef bgm::point<float, 2, bg::cs::cartesian> CollisionPoint; -typedef bgm::box<CollisionPoint> Box; -typedef std::tuple<Box, CollisionBox, IndexedSubfeature> CollisionTreeBox; -typedef bgi::rtree<CollisionTreeBox, bgi::linear<16, 4>> Tree; +using CollisionPoint = bgm::point<float, 2, bg::cs::cartesian>; +using Box = bgm::box<CollisionPoint>; +using CollisionTreeBox = std::tuple<Box, CollisionBox, IndexedSubfeature>; +using Tree = bgi::rtree<CollisionTreeBox, bgi::linear<16, 4>>; class IndexedSubfeature; @@ -49,8 +49,8 @@ public: const PlacementConfig config; - const float minScale = 0.5f; - const float maxScale = 2.0f; + float minScale = 0.5f; + float maxScale = 2.0f; float yStretch; std::array<float, 4> rotationMatrix; @@ -58,12 +58,14 @@ public: private: float findPlacementScale( - const Point<float>& anchor, const CollisionBox& box, + const Point<float>& anchor, const CollisionBox& box, const float boxMaxScale, const Point<float>& blockingAnchor, const CollisionBox& blocking); Box getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale = 1.0); Tree tree; Tree ignoredTree; + + float perspectiveRatio; }; } // namespace mbgl diff --git a/src/mbgl/text/get_anchors.cpp b/src/mbgl/text/get_anchors.cpp index 82702b20f0..d41faf2a71 100644 --- a/src/mbgl/text/get_anchors.cpp +++ b/src/mbgl/text/get_anchors.cpp @@ -34,7 +34,7 @@ static Anchors resample(const GeometryCoordinates& line, const GeometryCoordinate& a = *(it); const GeometryCoordinate& b = *(it + 1); - const float segmentDist = util::dist<float>(a, b); + const auto segmentDist = util::dist<float>(a, b); const float angle = util::angle_to(b, a); while (markedDistance + spacing < distance + segmentDist) { diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp index 9cf39de840..6cccb72ebe 100644 --- a/src/mbgl/text/glyph.hpp +++ b/src/mbgl/text/glyph.hpp @@ -5,6 +5,9 @@ #include <mbgl/util/rect.hpp> #include <mbgl/util/traits.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/util.hpp> #include <cstdint> #include <vector> @@ -13,8 +16,8 @@ namespace mbgl { -typedef char16_t GlyphID; -typedef std::set<GlyphID> GlyphIDs; +using GlyphID = char16_t; +using GlyphIDs = std::set<GlyphID>; // Note: this only works for the BMP GlyphRange getGlyphRange(GlyphID glyph); @@ -35,37 +38,47 @@ inline bool operator==(const GlyphMetrics& lhs, const GlyphMetrics& rhs) { lhs.advance == rhs.advance; } -struct Glyph { - Rect<uint16_t> rect; +class Glyph { +public: + // We're using this value throughout the Mapbox GL ecosystem. If this is different, the glyphs + // also need to be reencoded. + static constexpr const uint8_t borderSize = 3; + + GlyphID id = 0; + + // A signed distance field of the glyph with a border (see above). + AlphaImage bitmap; + + // Glyph metrics GlyphMetrics metrics; }; -typedef std::map<GlyphID, optional<Glyph>> GlyphPositions; -typedef std::map<FontStack, GlyphPositions> GlyphPositionMap; +using Glyphs = std::map<GlyphID, optional<Immutable<Glyph>>>; +using GlyphMap = std::map<FontStack, Glyphs>; class PositionedGlyph { public: - explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, float angle_) - : glyph(glyph_), x(x_), y(y_), angle(angle_) {} + explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_) + : glyph(glyph_), x(x_), y(y_), vertical(vertical_) {} GlyphID glyph = 0; float x = 0; float y = 0; - float angle = 0; + bool vertical = false; }; enum class WritingModeType : uint8_t; class Shaping { public: - explicit Shaping() : top(0), bottom(0), left(0), right(0) {} + explicit Shaping() = default; explicit Shaping(float x, float y, WritingModeType writingMode_) : top(y), bottom(y), left(x), right(x), writingMode(writingMode_) {} std::vector<PositionedGlyph> positionedGlyphs; - int32_t top; - int32_t bottom; - int32_t left; - int32_t right; + int32_t top = 0; + int32_t bottom = 0; + int32_t left = 0; + int32_t right = 0; WritingModeType writingMode; explicit operator bool() const { return !positionedGlyphs.empty(); } @@ -77,28 +90,27 @@ enum class WritingModeType : uint8_t { Vertical = 1 << 1, }; -constexpr WritingModeType operator|(WritingModeType a, WritingModeType b) { +MBGL_CONSTEXPR WritingModeType operator|(WritingModeType a, WritingModeType b) { return WritingModeType(mbgl::underlying_type(a) | mbgl::underlying_type(b)); } -constexpr WritingModeType& operator|=(WritingModeType& a, WritingModeType b) { +MBGL_CONSTEXPR WritingModeType& operator|=(WritingModeType& a, WritingModeType b) { return (a = a | b); } -constexpr bool operator&(WritingModeType lhs, WritingModeType rhs) { +MBGL_CONSTEXPR bool operator&(WritingModeType lhs, WritingModeType rhs) { return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs); } -constexpr WritingModeType& operator&=(WritingModeType& lhs, WritingModeType rhs) { +MBGL_CONSTEXPR WritingModeType& operator&=(WritingModeType& lhs, WritingModeType rhs) { return (lhs = WritingModeType(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs))); } -constexpr WritingModeType operator~(WritingModeType value) { +MBGL_CONSTEXPR WritingModeType operator~(WritingModeType value) { return WritingModeType(~mbgl::underlying_type(value)); } -typedef std::map<FontStack,GlyphIDs> GlyphDependencies; -typedef std::map<FontStack,GlyphRangeSet> GlyphRangeDependencies; - +using GlyphDependencies = std::map<FontStack,GlyphIDs>; +using GlyphRangeDependencies = std::map<FontStack,GlyphRangeSet>; } // end namespace mbgl diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp index 4feaab01f9..1b98ea36bf 100644 --- a/src/mbgl/text/glyph_atlas.cpp +++ b/src/mbgl/text/glyph_atlas.cpp @@ -1,262 +1,65 @@ #include <mbgl/text/glyph_atlas.hpp> -#include <mbgl/text/glyph_atlas_observer.hpp> -#include <mbgl/text/glyph_pbf.hpp> -#include <mbgl/gl/context.hpp> -#include <mbgl/util/logging.hpp> -#include <mbgl/util/platform.hpp> -#include <mbgl/storage/file_source.hpp> -#include <mbgl/storage/resource.hpp> -#include <mbgl/storage/response.hpp> -#include <cassert> -#include <algorithm> +#include <mapbox/shelf-pack.hpp> namespace mbgl { -static GlyphAtlasObserver nullObserver; +static constexpr uint32_t padding = 1; -GlyphAtlas::GlyphAtlas(const Size size, FileSource& fileSource_) - : fileSource(fileSource_), - observer(&nullObserver), - bin(size.width, size.height), - image(size), - dirty(true) { -} - -GlyphAtlas::~GlyphAtlas() = default; - -void GlyphAtlas::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies) { - auto dependencies = std::make_shared<GlyphDependencies>(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; - Entry& entry = entries[fontStack]; - - const GlyphIDs& glyphIDs = dependency.second; - GlyphRangeSet ranges; - for (const auto& glyphID : glyphIDs) { - ranges.insert(getGlyphRange(glyphID)); - } - - for (const auto& range : ranges) { - auto it = entry.ranges.find(range); - if (it == entry.ranges.end() || !it->second.parsed) { - GlyphRequest& request = requestRange(entry, fontStack, range); - request.requestors[&requestor] = dependencies; - } - } - } - - // 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); - } -} - -GlyphAtlas::GlyphRequest& GlyphAtlas::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) { - GlyphRequest& request = entry.ranges[range]; - - if (request.req) { - return request; - } - - request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) { - processResponse(res, fontStack, range); - }); +GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) { + GlyphAtlas result; - return request; -} + mapbox::ShelfPack::ShelfPackOptions options; + options.autoResize = true; + mapbox::ShelfPack pack(0, 0, options); -void GlyphAtlas::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) { - if (res.error) { - observer->onGlyphsError(fontStack, range, std::make_exception_ptr(std::runtime_error(res.error->message))); - return; - } + for (const auto& glyphMapEntry : glyphs) { + const FontStack& fontStack = glyphMapEntry.first; + GlyphPositionMap& positions = result.positions[fontStack]; - if (res.notModified) { - return; - } + for (const auto& entry : glyphMapEntry.second) { + if (entry.second && (*entry.second)->bitmap.valid()) { + const Glyph& glyph = **entry.second; - Entry& entry = entries[fontStack]; - GlyphRequest& request = entry.ranges[range]; + const mapbox::Bin& bin = *pack.packOne(-1, + glyph.bitmap.size.width + 2 * padding, + glyph.bitmap.size.height + 2 * padding); - if (!res.noContent) { - std::vector<SDFGlyph> glyphs; - - try { - glyphs = parseGlyphPBF(range, *res.data); - } catch (...) { - observer->onGlyphsError(fontStack, range, std::current_exception()); - return; - } - - for (auto& glyph : glyphs) { - auto it = entry.glyphs.find(glyph.id); - if (it == entry.glyphs.end()) { - // Glyph doesn't exist yet. - entry.glyphs.emplace(glyph.id, GlyphValue { - std::move(glyph.bitmap), - std::move(glyph.metrics), - {}, {} + result.image.resize({ + static_cast<uint32_t>(pack.width()), + static_cast<uint32_t>(pack.height()) }); - } else if (it->second.metrics == glyph.metrics) { - if (it->second.bitmap != glyph.bitmap) { - // The actual bitmap was updated; this is unsupported. - Log::Warning(Event::Glyph, "Modified glyph changed bitmap represenation"); - } - // At least try to update it in case it's currently unused. - // If it is already used, we won't attempt to update the glyph atlas texture. - it->second.bitmap = std::move(glyph.bitmap); - } else { - // The metrics were updated; this is unsupported. - Log::Warning(Event::Glyph, "Modified glyph has different metrics"); - return; - } - } - } - - request.parsed = true; - - for (auto& pair : request.requestors) { - GlyphRequestor& requestor = *pair.first; - const std::shared_ptr<GlyphDependencies>& dependencies = pair.second; - if (dependencies.unique()) { - addGlyphs(requestor, *dependencies); - } - } - - request.requestors.clear(); - - observer->onGlyphsLoaded(fontStack, range); -} - -void GlyphAtlas::setObserver(GlyphAtlasObserver* observer_) { - observer = observer_ ? observer_ : &nullObserver; -} -void GlyphAtlas::addGlyphs(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies) { - GlyphPositionMap glyphPositions; - - for (const auto& dependency : glyphDependencies) { - const FontStack& fontStack = dependency.first; - const GlyphIDs& glyphIDs = dependency.second; - - GlyphPositions& positions = glyphPositions[fontStack]; - Entry& entry = entries[fontStack]; - - for (const auto& glyphID : glyphIDs) { - // Make a glyph position entry even if we didn't get an SDF for the glyph. During layout, - // an empty optional is treated as "loaded but nothing to show", wheras no entry in the - // positions map means "not loaded yet". - optional<Glyph>& glyph = positions[glyphID]; - - auto it = entry.glyphs.find(glyphID); - if (it == entry.glyphs.end()) - continue; - - it->second.ids.insert(&requestor); - - glyph = Glyph { - addGlyph(it->second), - it->second.metrics - }; - } - } - - requestor.onGlyphsAvailable(glyphPositions); -} - -Rect<uint16_t> GlyphAtlas::addGlyph(GlyphValue& value) { - // The glyph is already in this texture. - if (value.rect) { - return *value.rect; - } - - // We don't need to add glyphs without a bitmap (e.g. whitespace). - if (!value.bitmap.valid()) { - return {}; - } - - // Add a 1px border around every image. - const uint32_t padding = 1; - uint16_t width = value.bitmap.size.width + 2 * padding; - uint16_t height = value.bitmap.size.height + 2 * padding; - - // Increase to next number divisible by 4, but at least 1. - // This is so we can scale down the texture coordinates and pack them - // into 2 bytes rather than 4 bytes. - width += (4 - width % 4); - height += (4 - height % 4); - - Rect<uint16_t> rect = bin.allocate(width, height); - if (rect.w == 0) { - Log::Error(Event::OpenGL, "glyph bitmap overflow"); - return {}; - } - - AlphaImage::copy(value.bitmap, image, { 0, 0 }, { rect.x + padding, rect.y + padding }, value.bitmap.size); - value.rect = rect; - dirty = true; - - return rect; -} - -void GlyphAtlas::removeGlyphValues(GlyphRequestor& requestor, std::map<GlyphID, GlyphValue>& values) { - for (auto it = values.begin(); it != values.end(); it++) { - GlyphValue& value = it->second; - if (value.ids.erase(&requestor) && value.ids.empty() && value.rect) { - const Rect<uint16_t>& rect = *value.rect; - - // Clear out the bitmap. - uint8_t *target = image.data.get(); - for (uint32_t y = 0; y < rect.h; y++) { - uint32_t y1 = image.size.width * (rect.y + y) + rect.x; - for (uint32_t x = 0; x < rect.w; x++) { - target[y1 + x] = 0; - } + AlphaImage::copy(glyph.bitmap, + result.image, + { 0, 0 }, + { + bin.x + padding, + bin.y + padding + }, + glyph.bitmap.size); + + positions.emplace(glyph.id, + GlyphPosition { + Rect<uint16_t> { + static_cast<uint16_t>(bin.x), + static_cast<uint16_t>(bin.y), + static_cast<uint16_t>(bin.w), + static_cast<uint16_t>(bin.h) + }, + glyph.metrics + }); } - - bin.release(rect); - value.rect = {}; } } -} - -void GlyphAtlas::removePendingRanges(mbgl::GlyphRequestor& requestor, std::map<GlyphRange, GlyphRequest>& ranges) { - for (auto it = ranges.begin(); it != ranges.end(); it++) { - it->second.requestors.erase(&requestor); - } -} -void GlyphAtlas::removeGlyphs(GlyphRequestor& requestor) { - for (auto& entry : entries) { - removeGlyphValues(requestor, entry.second.glyphs); - removePendingRanges(requestor, entry.second.ranges); - } -} - -Size GlyphAtlas::getSize() const { - return image.size; -} - -void GlyphAtlas::upload(gl::Context& context, gl::TextureUnit unit) { - if (!texture) { - texture = context.createTexture(image, unit); - } else if (dirty) { - context.updateTexture(*texture, image, unit); - } - - dirty = false; -} + pack.shrink(); + result.image.resize({ + static_cast<uint32_t>(pack.width()), + static_cast<uint32_t>(pack.height()) + }); -void GlyphAtlas::bind(gl::Context& context, gl::TextureUnit unit) { - upload(context, unit); - context.bindTexture(*texture, unit, gl::TextureFilter::Linear); + return result; } } // namespace mbgl diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp index ad9cf35adc..bb9115e4b4 100644 --- a/src/mbgl/text/glyph_atlas.hpp +++ b/src/mbgl/text/glyph_atlas.hpp @@ -1,110 +1,25 @@ #pragma once #include <mbgl/text/glyph.hpp> -#include <mbgl/text/glyph_atlas_observer.hpp> -#include <mbgl/text/glyph_range.hpp> -#include <mbgl/geometry/binpack.hpp> -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/optional.hpp> -#include <mbgl/util/font_stack.hpp> -#include <mbgl/util/work_queue.hpp> -#include <mbgl/util/image.hpp> -#include <mbgl/gl/texture.hpp> -#include <mbgl/gl/object.hpp> -#include <string> -#include <unordered_set> -#include <unordered_map> - -class GlyphAtlasTest; +#include <mapbox/shelf-pack.hpp> namespace mbgl { -class FileSource; -class AsyncRequest; -class Response; - -namespace gl { -class Context; -} // namespace gl - -class GlyphRequestor { -public: - virtual ~GlyphRequestor() = default; - virtual void onGlyphsAvailable(GlyphPositionMap) = 0; +struct GlyphPosition { + Rect<uint16_t> rect; + GlyphMetrics metrics; }; - -class GlyphAtlas : public util::noncopyable { -public: - GlyphAtlas(Size, FileSource&); - ~GlyphAtlas(); - - // Workers send a `getGlyphs` message to the main thread once they have determined - // which glyphs they will need. Invoking this method will increment reference - // counts for all the glyphs in `GlyphDependencies`. If all glyphs are already - // 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&, GlyphDependencies); - void removeGlyphs(GlyphRequestor&); - - void setURL(const std::string& url) { - glyphURL = url; - } - - void setObserver(GlyphAtlasObserver*); - - // Binds the atlas texture to the GPU, and uploads data if it is out of date. - void bind(gl::Context&, gl::TextureUnit unit); - - // Uploads the texture to the GPU to be available when we need it. This is a lazy operation; - // the texture is only bound when the data is out of date (=dirty). - void upload(gl::Context&, gl::TextureUnit unit); - - Size getSize() const; -private: - FileSource& fileSource; - std::string glyphURL; +using GlyphPositionMap = std::map<GlyphID, GlyphPosition>; +using GlyphPositions = std::map<FontStack, GlyphPositionMap>; - struct GlyphValue { - AlphaImage bitmap; - GlyphMetrics metrics; - optional<Rect<uint16_t>> rect; - std::unordered_set<GlyphRequestor*> ids; - }; - - struct GlyphRequest { - bool parsed = false; - std::unique_ptr<AsyncRequest> req; - std::unordered_map<GlyphRequestor*, std::shared_ptr<GlyphDependencies>> requestors; - }; - - struct Entry { - std::map<GlyphRange, GlyphRequest> ranges; - std::map<GlyphID, GlyphValue> glyphs; - }; - - std::unordered_map<FontStack, Entry, FontStackHash> entries; - - GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&); - void processResponse(const Response&, const FontStack&, const GlyphRange&); - - void addGlyphs(GlyphRequestor&, const GlyphDependencies&); - Rect<uint16_t> addGlyph(GlyphValue&); - - void removeGlyphValues(GlyphRequestor&, std::map<GlyphID, GlyphValue>&); - void removePendingRanges(GlyphRequestor&, std::map<GlyphRange, GlyphRequest>&); - - GlyphAtlasObserver* observer = nullptr; - - BinPack<uint16_t> bin; +class GlyphAtlas { +public: AlphaImage image; - bool dirty; - mbgl::optional<gl::Texture> texture; + GlyphPositions positions; }; +GlyphAtlas makeGlyphAtlas(const GlyphMap&); + } // namespace mbgl diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp new file mode 100644 index 0000000000..916d39ae62 --- /dev/null +++ b/src/mbgl/text/glyph_manager.cpp @@ -0,0 +1,145 @@ +#include <mbgl/text/glyph_manager.hpp> +#include <mbgl/text/glyph_manager_observer.hpp> +#include <mbgl/text/glyph_pbf.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> + +namespace mbgl { + +static GlyphManagerObserver nullObserver; + +GlyphManager::GlyphManager(FileSource& fileSource_) + : fileSource(fileSource_), + observer(&nullObserver) { +} + +GlyphManager::~GlyphManager() = default; + +void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies) { + auto dependencies = std::make_shared<GlyphDependencies>(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; + Entry& entry = entries[fontStack]; + + const GlyphIDs& glyphIDs = dependency.second; + GlyphRangeSet ranges; + for (const auto& glyphID : glyphIDs) { + ranges.insert(getGlyphRange(glyphID)); + } + + for (const auto& range : ranges) { + auto it = entry.ranges.find(range); + if (it == entry.ranges.end() || !it->second.parsed) { + GlyphRequest& request = requestRange(entry, fontStack, range); + request.requestors[&requestor] = dependencies; + } + } + } + + // If the shared dependencies pointer is already unique, then all dependent glyph ranges + // have already been loaded. Send a notification immediately. + if (dependencies.unique()) { + notify(requestor, *dependencies); + } +} + +GlyphManager::GlyphRequest& GlyphManager::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) { + GlyphRequest& request = entry.ranges[range]; + + if (request.req) { + return request; + } + + request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) { + processResponse(res, fontStack, range); + }); + + return request; +} + +void GlyphManager::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) { + if (res.error) { + observer->onGlyphsError(fontStack, range, std::make_exception_ptr(std::runtime_error(res.error->message))); + return; + } + + if (res.notModified) { + return; + } + + Entry& entry = entries[fontStack]; + GlyphRequest& request = entry.ranges[range]; + + if (!res.noContent) { + std::vector<Glyph> glyphs; + + try { + glyphs = parseGlyphPBF(range, *res.data); + } catch (...) { + observer->onGlyphsError(fontStack, range, std::current_exception()); + return; + } + + for (auto& glyph : glyphs) { + entry.glyphs.erase(glyph.id); + entry.glyphs.emplace(glyph.id, makeMutable<Glyph>(std::move(glyph))); + } + } + + request.parsed = true; + + for (auto& pair : request.requestors) { + GlyphRequestor& requestor = *pair.first; + const std::shared_ptr<GlyphDependencies>& dependencies = pair.second; + if (dependencies.unique()) { + notify(requestor, *dependencies); + } + } + + request.requestors.clear(); + + observer->onGlyphsLoaded(fontStack, range); +} + +void GlyphManager::setObserver(GlyphManagerObserver* observer_) { + observer = observer_ ? observer_ : &nullObserver; +} + +void GlyphManager::notify(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies) { + GlyphMap response; + + for (const auto& dependency : glyphDependencies) { + const FontStack& fontStack = dependency.first; + const GlyphIDs& glyphIDs = dependency.second; + + Glyphs& glyphs = response[fontStack]; + Entry& entry = entries[fontStack]; + + for (const auto& glyphID : glyphIDs) { + auto it = entry.glyphs.find(glyphID); + if (it != entry.glyphs.end()) { + glyphs.emplace(*it); + } else { + glyphs.emplace(glyphID, std::experimental::nullopt); + } + } + } + + requestor.onGlyphsAvailable(response); +} + +void GlyphManager::removeRequestor(GlyphRequestor& requestor) { + for (auto& entry : entries) { + for (auto& range : entry.second.ranges) { + range.second.requestors.erase(&requestor); + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp new file mode 100644 index 0000000000..00df079462 --- /dev/null +++ b/src/mbgl/text/glyph_manager.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include <mbgl/text/glyph.hpp> +#include <mbgl/text/glyph_manager_observer.hpp> +#include <mbgl/text/glyph_range.hpp> +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/font_stack.hpp> +#include <mbgl/util/immutable.hpp> + +#include <string> +#include <unordered_map> + +namespace mbgl { + +class FileSource; +class AsyncRequest; +class Response; + +class GlyphRequestor { +public: + virtual ~GlyphRequestor() = default; + virtual void onGlyphsAvailable(GlyphMap) = 0; +}; + +class GlyphManager : public util::noncopyable { +public: + GlyphManager(FileSource&); + ~GlyphManager(); + + // Workers send a `getGlyphs` message to the main thread once they have determined + // their `GlyphDependencies`. If all glyphs are already locally available, GlyphManager + // will provide them to the requestor immediately. Otherwise, it makes a request on the + // FileSource is made for each range neeed, and notifies the observer when all are + // complete. + void getGlyphs(GlyphRequestor&, GlyphDependencies); + void removeRequestor(GlyphRequestor&); + + void setURL(const std::string& url) { + glyphURL = url; + } + + void setObserver(GlyphManagerObserver*); + +private: + FileSource& fileSource; + std::string glyphURL; + + struct GlyphRequest { + bool parsed = false; + std::unique_ptr<AsyncRequest> req; + std::unordered_map<GlyphRequestor*, std::shared_ptr<GlyphDependencies>> requestors; + }; + + struct Entry { + std::map<GlyphRange, GlyphRequest> ranges; + std::map<GlyphID, Immutable<Glyph>> glyphs; + }; + + std::unordered_map<FontStack, Entry, FontStackHash> entries; + + GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&); + void processResponse(const Response&, const FontStack&, const GlyphRange&); + void notify(GlyphRequestor&, const GlyphDependencies&); + + GlyphManagerObserver* observer = nullptr; +}; + +} // namespace mbgl diff --git a/src/mbgl/text/glyph_atlas_observer.hpp b/src/mbgl/text/glyph_manager_observer.hpp index 9841017117..b8678e060a 100644 --- a/src/mbgl/text/glyph_atlas_observer.hpp +++ b/src/mbgl/text/glyph_manager_observer.hpp @@ -8,9 +8,9 @@ namespace mbgl { -class GlyphAtlasObserver { +class GlyphManagerObserver { public: - virtual ~GlyphAtlasObserver() = default; + virtual ~GlyphManagerObserver() = default; virtual void onGlyphsLoaded(const FontStack&, const GlyphRange&) {} virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) {} diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp index 033f50fe9c..cfaf803f75 100644 --- a/src/mbgl/text/glyph_pbf.cpp +++ b/src/mbgl/text/glyph_pbf.cpp @@ -4,8 +4,8 @@ namespace mbgl { -std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) { - std::vector<SDFGlyph> result; +std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) { + std::vector<Glyph> result; result.reserve(256); protozero::pbf_reader glyphs_pbf(data); @@ -15,7 +15,7 @@ std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::str while (fontstack_pbf.next(3)) { auto glyph_pbf = fontstack_pbf.get_message(); - SDFGlyph glyph; + Glyph glyph; protozero::data_view glyphData; bool hasID = false, hasWidth = false, hasHeight = false, hasLeft = false, @@ -73,8 +73,8 @@ std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::str // with the implicit border size, otherwise we expect there to be no bitmap at all. if (glyph.metrics.width && glyph.metrics.height) { const Size size { - glyph.metrics.width + 2 * SDFGlyph::borderSize, - glyph.metrics.height + 2 * SDFGlyph::borderSize + glyph.metrics.width + 2 * Glyph::borderSize, + glyph.metrics.height + 2 * Glyph::borderSize }; if (size.area() != glyphData.size()) { diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp index 162aeed93a..28a28b4114 100644 --- a/src/mbgl/text/glyph_pbf.hpp +++ b/src/mbgl/text/glyph_pbf.hpp @@ -2,28 +2,12 @@ #include <mbgl/text/glyph.hpp> #include <mbgl/text/glyph_range.hpp> -#include <mbgl/util/image.hpp> #include <string> #include <vector> namespace mbgl { -class SDFGlyph { -public: - // We're using this value throughout the Mapbox GL ecosystem. If this is different, the glyphs - // also need to be reencoded. - static constexpr const uint8_t borderSize = 3; - - GlyphID id = 0; - - // A signed distance field of the glyph with a border (see above). - AlphaImage bitmap; - - // Glyph metrics - GlyphMetrics metrics; -}; - -std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange&, const std::string& data); +std::vector<Glyph> parseGlyphPBF(const GlyphRange&, const std::string& data); } // namespace mbgl diff --git a/src/mbgl/text/glyph_range.hpp b/src/mbgl/text/glyph_range.hpp index dd39e092b7..74afb73dfc 100644 --- a/src/mbgl/text/glyph_range.hpp +++ b/src/mbgl/text/glyph_range.hpp @@ -7,7 +7,7 @@ namespace mbgl { -typedef std::pair<uint16_t, uint16_t> GlyphRange; +using GlyphRange = std::pair<uint16_t, uint16_t>; struct GlyphRangeHash { std::size_t operator()(const GlyphRange& glyphRange) const { @@ -15,7 +15,7 @@ struct GlyphRangeHash { } }; -typedef std::unordered_set<GlyphRange, GlyphRangeHash> GlyphRangeSet; +using GlyphRangeSet = std::unordered_set<GlyphRange, GlyphRangeHash>; constexpr uint32_t GLYPHS_PER_GLYPH_RANGE = 256; constexpr uint32_t GLYPH_RANGES_PER_FONT_STACK = 256; diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp index 7e61cabc24..48b24b5f41 100644 --- a/src/mbgl/text/placement_config.hpp +++ b/src/mbgl/text/placement_config.hpp @@ -1,15 +1,21 @@ #pragma once +#include <mbgl/util/constants.hpp> + namespace mbgl { class PlacementConfig { public: - PlacementConfig(float angle_ = 0, float pitch_ = 0, bool debug_ = false) - : angle(angle_), pitch(pitch_), debug(debug_) { + PlacementConfig(float angle_ = 0, float pitch_ = 0, float cameraToCenterDistance_ = 0, float cameraToTileDistance_ = 0, bool debug_ = false) + : angle(angle_), pitch(pitch_), cameraToCenterDistance(cameraToCenterDistance_), cameraToTileDistance(cameraToTileDistance_), debug(debug_) { } bool operator==(const PlacementConfig& rhs) const { - return angle == rhs.angle && pitch == rhs.pitch && debug == rhs.debug; + return angle == rhs.angle && + pitch == rhs.pitch && + debug == rhs.debug && + ((pitch * util::RAD2DEG < 25) || + (cameraToCenterDistance == rhs.cameraToCenterDistance && cameraToTileDistance == rhs.cameraToTileDistance)); } bool operator!=(const PlacementConfig& rhs) const { @@ -19,6 +25,8 @@ public: public: float angle; float pitch; + float cameraToCenterDistance; + float cameraToTileDistance; bool debug; }; diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index e1a9699835..0014ae8d01 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -13,22 +13,21 @@ namespace mbgl { using namespace style; -const float globalMinScale = 0.5f; // underscale by 1 zoom level - -SymbolQuad getIconQuad(const Anchor& anchor, - const PositionedIcon& shapedIcon, - const GeometryCoordinates& line, +SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, const SymbolLayoutProperties::Evaluated& layout, const float layoutTextSize, - const style::SymbolPlacementType placement, const Shaping& shapedText) { - auto image = *shapedIcon.image(); + const ImagePosition& image = shapedIcon.image(); + // If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual + // pixels. The quad needs to be padded to account for this, otherwise they'll look slightly clipped + // on one edge in some cases. const float border = 1.0; - auto left = shapedIcon.left() - border; - auto right = left + image.pos.w / image.relativePixelRatio; - auto top = shapedIcon.top() - border; - auto bottom = top + image.pos.h / image.relativePixelRatio; + + float top = shapedIcon.top() - border / image.pixelRatio; + float left = shapedIcon.left() - border / image.pixelRatio; + float bottom = shapedIcon.bottom() + border / image.pixelRatio; + float right = shapedIcon.right() + border / image.pixelRatio; Point<float> tl; Point<float> tr; Point<float> br; @@ -67,18 +66,7 @@ SymbolQuad getIconQuad(const Anchor& anchor, bl = {left, bottom}; } - float angle = shapedIcon.angle(); - if (placement == style::SymbolPlacementType::Line) { - assert(static_cast<unsigned int>(anchor.segment) < line.size()); - const GeometryCoordinate &prev= line[anchor.segment]; - if (anchor.point.y == prev.y && anchor.point.x == prev.x && - static_cast<unsigned int>(anchor.segment + 1) < line.size()) { - const GeometryCoordinate &next= line[anchor.segment + 1]; - angle += std::atan2(anchor.point.y - next.y, anchor.point.x - next.x) + M_PI; - } else { - angle += std::atan2(anchor.point.y - prev.y, anchor.point.x - prev.x); - } - } + const float angle = shapedIcon.angle(); if (angle) { // Compute the transformation matrix. @@ -92,283 +80,95 @@ SymbolQuad getIconQuad(const Anchor& anchor, br = util::matrixMultiply(matrix, br); } - return SymbolQuad { tl, tr, bl, br, image.pos, 0, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity(), shapedText.writingMode }; -} - -struct GlyphInstance { - explicit GlyphInstance(Point<float> anchorPoint_) : anchorPoint(std::move(anchorPoint_)) {} - explicit GlyphInstance(Point<float> anchorPoint_, bool upsideDown_, float minScale_, float maxScale_, - float angle_) - : anchorPoint(std::move(anchorPoint_)), upsideDown(upsideDown_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {} - - const Point<float> anchorPoint; - const bool upsideDown = false; - const float minScale = globalMinScale; - const float maxScale = std::numeric_limits<float>::infinity(); - const float angle = 0.0f; -}; - -typedef std::vector<GlyphInstance> GlyphInstances; - -struct VirtualSegment { - Point<float> anchor; - Point<float> end; - size_t index; - float minScale; - float maxScale; -}; - -inline void insertSegmentGlyph(std::back_insert_iterator<GlyphInstances> glyphs, - const VirtualSegment& virtualSegment, - const bool glyphIsLogicallyForward, - const bool upsideDown) { - float segmentAngle = std::atan2(virtualSegment.end.y - virtualSegment.anchor.y, virtualSegment.end.x - virtualSegment.anchor.x); - // If !glyphIsLogicallyForward, we're iterating through the segments in reverse logical order as well, so we need to flip the segment angle - float glyphAngle = glyphIsLogicallyForward ? segmentAngle : segmentAngle + M_PI; - - // Insert a glyph rotated at this angle for display in the range from [scale, previous(larger) scale]. - glyphs = GlyphInstance{ - /* anchor */ virtualSegment.anchor, - /* upsideDown */ upsideDown, - /* minScale */ virtualSegment.minScale, - /* maxScale */ virtualSegment.maxScale, - /* angle */ static_cast<float>(std::fmod((glyphAngle + 2.0 * M_PI), (2.0 * M_PI)))}; -} - -/** - Given the distance along the line from the label anchor to the beginning of the current segment, - project a "virtual anchor" point at the same distance along the line extending out from this segment. - - B <-- beginning of current segment -* . . . . . . . *--------* E <-- end of current segment -VA | - / VA = "virtual segment anchor" - / - ---*-----` - A = label anchor - - Distance _along line_ from A to B == straight-line distance from VA to B. - */ -inline Point<float> getVirtualSegmentAnchor(const Point<float>& segmentBegin, const Point<float>& segmentEnd, float distanceFromAnchorToSegmentBegin) { - Point<float> segmentDirectionUnitVector = util::normal<float>(segmentBegin, segmentEnd); - return segmentBegin - (segmentDirectionUnitVector * distanceFromAnchorToSegmentBegin); -} - -/* - Given the segment joining `segmentAnchor` and `segmentEnd` and a desired offset - `glyphDistanceFromAnchor` at which a glyph is to be placed, calculate the minimum - "scale" at which the glyph will fall on the segment (i.e., not past the end) - - "Scale" here refers to the ratio between the *rendered* zoom level and the text-layout - zoom level, which is 1 + (source tile's zoom level). `glyphDistanceFromAnchor`, although - passed in units consistent with the text-layout zoom level, is based on text size. So - when the tile is being rendered at z < text-layout zoom, the glyph's actual distance from - the anchor is larger relative to the segment's length than at layout time: - - - GLYPH - z == layout-zoom, scale == 1: segmentAnchor *--------------^-------------* segmentEnd - z == layout-zoom - 1, scale == 0.5: segmentAnchor *--------------^* segmentEnd - - <--------------> - Anchor-to-glyph distance stays visually fixed, - so it changes relative to the segment. -*/ -inline float getMinScaleForSegment(const float glyphDistanceFromAnchor, - const Point<float>& segmentAnchor, - const Point<float>& segmentEnd) { - const float distanceFromAnchorToEnd = util::dist<float>(segmentAnchor, segmentEnd); - return glyphDistanceFromAnchor / distanceFromAnchorToEnd; -} - -inline Point<float> getSegmentEnd(const bool glyphIsLogicallyForward, - const GeometryCoordinates& line, - const size_t segmentIndex) { - return convertPoint<float>(glyphIsLogicallyForward ? line[segmentIndex+1] : line[segmentIndex]); -} - -optional<VirtualSegment> getNextVirtualSegment(const VirtualSegment& previousVirtualSegment, - const GeometryCoordinates& line, - const float glyphDistanceFromAnchor, - const bool glyphIsLogicallyForward) { - auto nextSegmentBegin = previousVirtualSegment.end; - - auto end = nextSegmentBegin; - size_t index = previousVirtualSegment.index; - - // skip duplicate nodes - while (end == nextSegmentBegin) { - // look ahead by 2 points in the line because the segment index refers to the beginning - // of the segment, and we need an endpoint too - if (glyphIsLogicallyForward && (index + 2 < line.size())) { - index += 1; - } else if (!glyphIsLogicallyForward && index != 0) { - index -= 1; - } else { - return {}; - } - - end = getSegmentEnd(glyphIsLogicallyForward, line, index); - } - - const auto anchor = getVirtualSegmentAnchor(nextSegmentBegin, end, - util::dist<float>(previousVirtualSegment.anchor, - previousVirtualSegment.end)); - return VirtualSegment { - anchor, - end, - index, - getMinScaleForSegment(glyphDistanceFromAnchor, anchor, end), - previousVirtualSegment.minScale - }; -} - -/* - Given (1) a glyph positioned relative to an anchor point and (2) a line to follow, - calculates which segment of the line the glyph will fall on for each possible - scale range, and for each range produces a "virtual" anchor point and an angle that will - place the glyph on the right segment and rotated to the correct angle. - - Because one glyph quad is made ahead of time for each possible orientation, the - symbol_sdf shader can quickly handle changing layout as we zoom in and out - - If the "keepUpright" property is set, we call getLineGlyphs twice (once upright and - once "upside down"). This will generate two sets of glyphs following the line in opposite - directions. Later, SymbolLayout::place will look at the glyphs and based on the placement - angle determine if their original anchor was "upright" or not -- based on that, it throws - away one set of glyphs or the other (this work has to be done in the CPU, but it's just a - filter so it's fast) - */ -void getLineGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, - Anchor& anchor, - float glyphHorizontalOffsetFromAnchor, - const GeometryCoordinates& line, - size_t anchorSegment, - bool upsideDown) { - assert(line.size() > anchorSegment+1); - - // This is true if the glyph is "logically forward" of the anchor point, based on the ordering of line segments - // The actual angle of the line is irrelevant - // If "upsideDown" is set, everything is flipped - const bool glyphIsLogicallyForward = (glyphHorizontalOffsetFromAnchor >= 0) ^ upsideDown; - const float glyphDistanceFromAnchor = std::fabs(glyphHorizontalOffsetFromAnchor); - - const auto initialSegmentEnd = getSegmentEnd(glyphIsLogicallyForward, line, anchorSegment); - VirtualSegment virtualSegment = { - anchor.point, - initialSegmentEnd, - anchorSegment, - getMinScaleForSegment(glyphDistanceFromAnchor, anchor.point, initialSegmentEnd), - std::numeric_limits<float>::infinity() + // Icon quad is padded, so texture coordinates also need to be padded. + Rect<uint16_t> textureRect { + static_cast<uint16_t>(image.textureRect.x - border), + static_cast<uint16_t>(image.textureRect.y - border), + static_cast<uint16_t>(image.textureRect.w + border * 2), + static_cast<uint16_t>(image.textureRect.h + border * 2) }; - - while (true) { - insertSegmentGlyph(glyphs, - virtualSegment, - glyphIsLogicallyForward, - upsideDown); - - if (virtualSegment.minScale <= anchor.scale) { - // No need to calculate below the scale where the label starts showing - return; - } - - optional<VirtualSegment> nextVirtualSegment = getNextVirtualSegment(virtualSegment, - line, - glyphDistanceFromAnchor, - glyphIsLogicallyForward); - if (!nextVirtualSegment) { - // There are no more segments, so we can't fit this glyph on the line at a lower scale - // This implies we can't show the label at all at lower scale, so we update the anchor's min scale - anchor.scale = virtualSegment.minScale; - return; - } else { - virtualSegment = *nextVirtualSegment; - } - } - + + return SymbolQuad { tl, tr, bl, br, textureRect, shapedText.writingMode, { 0.0f, 0.0f } }; } -SymbolQuads getGlyphQuads(Anchor& anchor, - const Shaping& shapedText, - const float boxScale, - const GeometryCoordinates& line, +SymbolQuads getGlyphQuads(const Shaping& shapedText, const SymbolLayoutProperties::Evaluated& layout, const style::SymbolPlacementType placement, - const GlyphPositions& face) { + const GlyphPositionMap& positions) { const float textRotate = layout.get<TextRotate>() * util::DEG2RAD; - const bool keepUpright = layout.get<TextKeepUpright>(); + + const float oneEm = 24.0; + std::array<float, 2> textOffset = layout.get<TextOffset>(); + textOffset[0] *= oneEm; + textOffset[1] *= oneEm; SymbolQuads quads; for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) { - auto face_it = face.find(positionedGlyph.glyph); - if (face_it == face.end() || !face_it->second || !(*face_it->second).rect.hasArea()) + auto positionsIt = positions.find(positionedGlyph.glyph); + if (positionsIt == positions.end()) continue; - const Glyph& glyph = *face_it->second; + const GlyphPosition& glyph = positionsIt->second; const Rect<uint16_t>& rect = glyph.rect; - const float centerX = (positionedGlyph.x + glyph.metrics.advance / 2.0f) * boxScale; - - GlyphInstances glyphInstances; - if (placement == style::SymbolPlacementType::Line) { - getLineGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, false); - if (keepUpright) - getLineGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, true); - } else { - glyphInstances.emplace_back(GlyphInstance{anchor.point}); - } // The rects have an addditional buffer that is not included in their size; const float glyphPadding = 1.0f; const float rectBuffer = 3.0f + glyphPadding; - const float x1 = positionedGlyph.x + glyph.metrics.left - rectBuffer; - const float y1 = positionedGlyph.y - glyph.metrics.top - rectBuffer; - const float x2 = x1 + rect.w; - const float y2 = y1 + rect.h; + const float halfAdvance = glyph.metrics.advance / 2.0; + const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement == SymbolPlacementType::Line; - const Point<float> center{positionedGlyph.x, static_cast<float>(static_cast<float>(glyph.metrics.advance) / 2.0)}; + const Point<float> glyphOffset = alongLine ? + Point<float>{ positionedGlyph.x + halfAdvance, positionedGlyph.y } : + Point<float>{ 0.0f, 0.0f }; - Point<float> otl{x1, y1}; - Point<float> otr{x2, y1}; - Point<float> obl{x1, y2}; - Point<float> obr{x2, y2}; - - if (positionedGlyph.angle != 0) { - otl = util::rotate(otl - center, positionedGlyph.angle) + center; - otr = util::rotate(otr - center, positionedGlyph.angle) + center; - obl = util::rotate(obl - center, positionedGlyph.angle) + center; - obr = util::rotate(obr - center, positionedGlyph.angle) + center; - } + const Point<float> builtInOffset = alongLine ? + Point<float>{ 0.0f, 0.0f } : + Point<float>{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] }; - for (const GlyphInstance &instance : glyphInstances) { - Point<float> tl = otl; - Point<float> tr = otr; - Point<float> bl = obl; - Point<float> br = obr; - if (textRotate) { - // Compute the transformation matrix. - float angle_sin = std::sin(textRotate); - float angle_cos = std::cos(textRotate); - std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; + const float x1 = glyph.metrics.left - rectBuffer - halfAdvance + builtInOffset.x; + const float y1 = -glyph.metrics.top - rectBuffer + builtInOffset.y; + const float x2 = x1 + rect.w; + const float y2 = y1 + rect.h; - tl = util::matrixMultiply(matrix, tl); - tr = util::matrixMultiply(matrix, tr); - bl = util::matrixMultiply(matrix, bl); - br = util::matrixMultiply(matrix, br); - } + Point<float> tl{x1, y1}; + Point<float> tr{x2, y1}; + Point<float> bl{x1, y2}; + Point<float> br{x2, y2}; + + if (alongLine && positionedGlyph.vertical) { + // Vertical-supporting glyphs are laid out in 24x24 point boxes (1 square em) + // In horizontal orientation, the y values for glyphs are below the midline + // and we use a "yOffset" of -17 to pull them up to the middle. + // By rotating counter-clockwise around the point at the center of the left + // edge of a 24x24 layout box centered below the midline, we align the center + // of the glyphs with the horizontal midline, so the yOffset is no longer + // necessary, but we also pull the glyph to the left along the x axis + const Point<float> center{-halfAdvance, halfAdvance}; + const float verticalRotation = -M_PI_2; + const Point<float> xOffsetCorrection{5, 0}; + + tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection; + tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection; + bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection; + br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection; + } - // Prevent label from extending past the end of the line - const float glyphMinScale = std::max(instance.minScale, anchor.scale); + if (textRotate) { + // Compute the transformation matrix. + float angle_sin = std::sin(textRotate); + float angle_cos = std::cos(textRotate); + std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; - // All the glyphs for a label are tagged with either the "right side up" or "upside down" anchor angle, - // which is used at placement time to determine which set to show - const float anchorAngle = std::fmod((anchor.angle + (instance.upsideDown ? M_PI : 0.0) + 2 * M_PI), (2 * M_PI)); - const float glyphAngle = std::fmod((instance.angle + (instance.upsideDown ? M_PI : 0.0) + 2 * M_PI), (2 * M_PI)); - quads.emplace_back(tl, tr, bl, br, rect, anchorAngle, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale, shapedText.writingMode); + tl = util::matrixMultiply(matrix, tl); + tr = util::matrixMultiply(matrix, tr); + bl = util::matrixMultiply(matrix, bl); + br = util::matrixMultiply(matrix, br); } + + quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset); } return quads; diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index 333000627b..33d003c935 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -1,6 +1,6 @@ #pragma once -#include <mbgl/text/glyph.hpp> +#include <mbgl/text/glyph_atlas.hpp> #include <mbgl/style/types.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> #include <mbgl/tile/geometry_tile_data.hpp> @@ -19,52 +19,35 @@ public: Point<float> bl_, Point<float> br_, Rect<uint16_t> tex_, - float anchorAngle_, - float glyphAngle_, - Point<float> anchorPoint_, - float minScale_, - float maxScale_, - WritingModeType writingMode_) + WritingModeType writingMode_, + Point<float> glyphOffset_) : tl(std::move(tl_)), tr(std::move(tr_)), bl(std::move(bl_)), br(std::move(br_)), tex(std::move(tex_)), - anchorAngle(anchorAngle_), - glyphAngle(glyphAngle_), - anchorPoint(std::move(anchorPoint_)), - minScale(minScale_), - maxScale(maxScale_), - writingMode(writingMode_) {} + writingMode(writingMode_), + glyphOffset(glyphOffset_) {} Point<float> tl; Point<float> tr; Point<float> bl; Point<float> br; Rect<uint16_t> tex; - float anchorAngle, glyphAngle; - Point<float> anchorPoint; - float minScale; - float maxScale; WritingModeType writingMode; + Point<float> glyphOffset; }; -typedef std::vector<SymbolQuad> SymbolQuads; +using SymbolQuads = std::vector<SymbolQuad>; -SymbolQuad getIconQuad(const Anchor& anchor, - const PositionedIcon& shapedIcon, - const GeometryCoordinates& line, +SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, const style::SymbolLayoutProperties::Evaluated&, const float layoutTextSize, - style::SymbolPlacementType placement, const Shaping& shapedText); -SymbolQuads getGlyphQuads(Anchor& anchor, - const Shaping& shapedText, - const float boxScale, - const GeometryCoordinates& line, +SymbolQuads getGlyphQuads(const Shaping& shapedText, const style::SymbolLayoutProperties::Evaluated&, style::SymbolPlacementType placement, - const GlyphPositions& face); + const GlyphPositionMap& positions); } // namespace mbgl diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index 78aa142c61..5d688ea539 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -7,22 +7,70 @@ #include <boost/algorithm/string.hpp> #include <algorithm> +#include <cmath> namespace mbgl { -optional<PositionedIcon> PositionedIcon::shapeIcon(const SpriteAtlasElement& image, const std::array<float, 2>& iconOffset, const float iconRotation) { - if (!image.pos.hasArea()) { - return {}; +struct AnchorAlignment { + AnchorAlignment(float horizontal_, float vertical_) + : horizontalAlign(horizontal_), verticalAlign(vertical_) { + } + + float horizontalAlign; + float verticalAlign; +}; + +AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor) { + float horizontalAlign = 0.5; + float verticalAlign = 0.5; + + switch (anchor) { + case style::SymbolAnchorType::Top: + case style::SymbolAnchorType::Bottom: + case style::SymbolAnchorType::Center: + break; + case style::SymbolAnchorType::Right: + case style::SymbolAnchorType::TopRight: + case style::SymbolAnchorType::BottomRight: + horizontalAlign = 1; + break; + case style::SymbolAnchorType::Left: + case style::SymbolAnchorType::TopLeft: + case style::SymbolAnchorType::BottomLeft: + horizontalAlign = 0; + break; + } + + switch (anchor) { + case style::SymbolAnchorType::Left: + case style::SymbolAnchorType::Right: + case style::SymbolAnchorType::Center: + break; + case style::SymbolAnchorType::Bottom: + case style::SymbolAnchorType::BottomLeft: + case style::SymbolAnchorType::BottomRight: + verticalAlign = 1; + break; + case style::SymbolAnchorType::Top: + case style::SymbolAnchorType::TopLeft: + case style::SymbolAnchorType::TopRight: + verticalAlign = 0; + break; } + return AnchorAlignment(horizontalAlign, verticalAlign); +} + +PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) { + AnchorAlignment anchorAlign = getAnchorAlignment(iconAnchor); float dx = iconOffset[0]; float dy = iconOffset[1]; - float x1 = dx - image.width/ 2.0f; - float x2 = x1 + image.width; - float y1 = dy - image.height / 2.0f; - float y2 = y1 + image.height; + float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign; + float x2 = x1 + image.displaySize()[0]; + float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign; + float y2 = y1 + image.displaySize()[1]; - return { PositionedIcon { image, y1, y2, x1, x2, iconRotation } }; + return PositionedIcon { image, y1, y2, x1, x2, iconRotation }; } void align(Shaping& shaping, @@ -31,12 +79,9 @@ void align(Shaping& shaping, const float verticalAlign, const float maxLineLength, const float lineHeight, - const std::size_t lineCount, - const Point<float>& translate) { - const float shiftX = - (justify - horizontalAlign) * maxLineLength + ::round(translate.x); - const float shiftY = - (-verticalAlign * lineCount + 0.5) * lineHeight + ::round(translate.y); + const std::size_t lineCount) { + const float shiftX = (justify - horizontalAlign) * maxLineLength; + const float shiftY = (-verticalAlign * lineCount + 0.5) * lineHeight; for (auto& glyph : shaping.positionedGlyphs) { glyph.x += shiftX; @@ -46,7 +91,7 @@ void align(Shaping& shaping, // justify left = 0, right = 1, center = .5 void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs, - const GlyphPositions& glyphs, + const Glyphs& glyphs, std::size_t start, std::size_t end, float justify) { @@ -57,7 +102,7 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs, PositionedGlyph& glyph = positionedGlyphs[end]; auto it = glyphs.find(glyph.glyph); if (it != glyphs.end() && it->second) { - const uint32_t lastAdvance = it->second->metrics.advance; + const uint32_t lastAdvance = (*it->second)->metrics.advance; const float lineIndent = float(glyph.x + lastAdvance) * justify; for (std::size_t j = start; j <= end; j++) { @@ -69,17 +114,17 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs, float determineAverageLineWidth(const std::u16string& logicalInput, const float spacing, float maxWidth, - const GlyphPositions& glyphs) { + const Glyphs& glyphs) { float totalWidth = 0; for (char16_t chr : logicalInput) { auto it = glyphs.find(chr); if (it != glyphs.end() && it->second) { - totalWidth += it->second->metrics.advance + spacing; + totalWidth += (*it->second)->metrics.advance + spacing; } } - int32_t targetLineCount = std::fmax(1, std::ceil(totalWidth / maxWidth)); + int32_t targetLineCount = ::fmax(1, std::ceil(totalWidth / maxWidth)); return totalWidth / targetLineCount; } @@ -168,7 +213,7 @@ std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput, const float spacing, float maxWidth, const WritingModeType writingMode, - const GlyphPositions& glyphs) { + const Glyphs& glyphs) { if (!maxWidth || writingMode != WritingModeType::Horizontal) { return {}; } @@ -186,7 +231,7 @@ std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput, const char16_t codePoint = logicalInput[i]; auto it = glyphs.find(codePoint); if (it != glyphs.end() && it->second && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) { - currentX += it->second->metrics.advance + spacing; + currentX += (*it->second)->metrics.advance + spacing; } // Ideographic characters, spaces, and word-breaking punctuation that often appear without @@ -206,13 +251,11 @@ void shapeLines(Shaping& shaping, const std::vector<std::u16string>& lines, const float spacing, const float lineHeight, - const float horizontalAlign, - const float verticalAlign, - const float justify, - const Point<float>& translate, + const style::SymbolAnchorType textAnchor, + const style::TextJustifyType textJustify, const float verticalHeight, const WritingModeType writingMode, - const GlyphPositions& glyphs) { + const Glyphs& glyphs) { // the y offset *should* be part of the font metadata const int32_t yOffset = -17; @@ -221,6 +264,10 @@ void shapeLines(Shaping& shaping, float y = yOffset; float maxLineLength = 0; + + const float justify = textJustify == style::TextJustifyType::Right ? 1 : + textJustify == style::TextJustifyType::Left ? 0 : + 0.5; for (std::u16string line : lines) { // Collapse whitespace so it doesn't throw off justification @@ -238,13 +285,13 @@ void shapeLines(Shaping& shaping, continue; } - const Glyph& glyph = *it->second; + const Glyph& glyph = **it->second; if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) { - shaping.positionedGlyphs.emplace_back(chr, x, y, 0); + shaping.positionedGlyphs.emplace_back(chr, x, y, false); x += glyph.metrics.advance + spacing; } else { - shaping.positionedGlyphs.emplace_back(chr, x, 0, -M_PI_2); + shaping.positionedGlyphs.emplace_back(chr, x, 0, true); x += verticalHeight + spacing; } } @@ -261,38 +308,39 @@ void shapeLines(Shaping& shaping, x = 0; y += lineHeight; } - - align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, - lines.size(), translate); + + auto anchorAlign = getAnchorAlignment(textAnchor); + + align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength, + lineHeight, lines.size()); const uint32_t height = lines.size() * lineHeight; - + // Calculate the bounding box - shaping.top += -verticalAlign * height; + shaping.top += -anchorAlign.verticalAlign * height; shaping.bottom = shaping.top + height; - shaping.left += -horizontalAlign * maxLineLength; + shaping.left += -anchorAlign.horizontalAlign * maxLineLength; shaping.right = shaping.left + maxLineLength; } const Shaping getShaping(const std::u16string& logicalInput, const float maxWidth, const float lineHeight, - const float horizontalAlign, - const float verticalAlign, - const float justify, + const style::SymbolAnchorType textAnchor, + const style::TextJustifyType textJustify, const float spacing, const Point<float>& translate, const float verticalHeight, const WritingModeType writingMode, BiDi& bidi, - const GlyphPositions& glyphs) { + const Glyphs& glyphs) { Shaping shaping(translate.x, translate.y, writingMode); std::vector<std::u16string> reorderedLines = bidi.processText(logicalInput, determineLineBreaks(logicalInput, spacing, maxWidth, writingMode, glyphs)); - shapeLines(shaping, reorderedLines, spacing, lineHeight, horizontalAlign, verticalAlign, - justify, translate, verticalHeight, writingMode, glyphs); + shapeLines(shaping, reorderedLines, spacing, lineHeight, textAnchor, + textJustify, verticalHeight, writingMode, glyphs); return shaping; } diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index b7eee5a5db..0a961849e5 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -1,32 +1,30 @@ #pragma once #include <mbgl/text/glyph.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/style/image.hpp> -#include <mbgl/util/optional.hpp> +#include <mbgl/renderer/image_atlas.hpp> +#include <mbgl/style/types.hpp> namespace mbgl { -class SpriteAtlasElement; class SymbolFeature; class BiDi; class PositionedIcon { private: - PositionedIcon(const SpriteAtlasElement& image_, + PositionedIcon(ImagePosition image_, float top_, float bottom_, float left_, float right_, float angle_) - : _image(image_), + : _image(std::move(image_)), _top(top_), _bottom(bottom_), _left(left_), _right(right_), _angle(angle_) {} - optional<SpriteAtlasElement> _image; + ImagePosition _image; float _top; float _bottom; float _left; @@ -34,9 +32,12 @@ private: float _angle; public: - static optional<PositionedIcon> shapeIcon(const class SpriteAtlasElement&, const std::array<float, 2>& iconOffset, const float iconRotation); + static PositionedIcon shapeIcon(const ImagePosition&, + const std::array<float, 2>& iconOffset, + style::SymbolAnchorType iconAnchor, + const float iconRotation); - optional<class SpriteAtlasElement> image() const { return _image; } + const ImagePosition& image() const { return _image; } float top() const { return _top; } float bottom() const { return _bottom; } float left() const { return _left; } @@ -47,14 +48,13 @@ public: const Shaping getShaping(const std::u16string& string, float maxWidth, float lineHeight, - float horizontalAlign, - float verticalAlign, - float justify, + style::SymbolAnchorType textAnchor, + style::TextJustifyType textJustify, float spacing, const Point<float>& translate, float verticalHeight, const WritingModeType, BiDi& bidi, - const GlyphPositions& glyphs); + const Glyphs& glyphs); } // namespace mbgl diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index 3c4939a0a6..5d8339d775 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -1,8 +1,9 @@ #include <mbgl/tile/geojson_tile.hpp> #include <mbgl/tile/geometry_tile_data.hpp> -#include <mbgl/map/query.hpp> -#include <mbgl/style/style.hpp> +#include <mbgl/renderer/query.hpp> #include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/style/filter_evaluator.hpp> +#include <mbgl/util/string.hpp> #include <mapbox/geojsonvt.hpp> #include <supercluster.hpp> @@ -52,43 +53,57 @@ public: } }; -class GeoJSONTileData : public GeometryTileData, - public GeometryTileLayer { +class GeoJSONTileLayer : public GeometryTileLayer { public: - mapbox::geometry::feature_collection<int16_t> features; - - GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_) + GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_) : features(std::move(features_)) { } - std::unique_ptr<GeometryTileData> clone() const override { - return std::make_unique<GeoJSONTileData>(*this); + std::size_t featureCount() const override { + return features->size(); } - const GeometryTileLayer* getLayer(const std::string&) const override { - return this; + std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override { + return std::make_unique<GeoJSONTileFeature>((*features)[i]); } std::string getName() const override { return ""; } - std::size_t featureCount() const override { - return features.size(); +private: + std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features; +}; + +class GeoJSONTileData : public GeometryTileData { +public: + GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_) + : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>( + std::move(features_))) { } - std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override { - return std::make_unique<GeoJSONTileFeature>(features[i]); + GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_) + : features(std::move(features_)) { + } + + std::unique_ptr<GeometryTileData> clone() const override { + return std::make_unique<GeoJSONTileData>(features); } + + std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override { + return std::make_unique<GeoJSONTileLayer>(features); + } + + +private: + std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features; }; GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, std::string sourceID_, const TileParameters& parameters, mapbox::geometry::feature_collection<int16_t> features) - : GeometryTile(overscaledTileID, sourceID_, parameters, - *parameters.style.glyphAtlas, - *parameters.style.spriteAtlas) { + : GeometryTile(overscaledTileID, sourceID_, parameters) { updateData(std::move(features)); } diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 23b3737a38..8c018ce3aa 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -6,20 +6,20 @@ #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/custom_layer.hpp> #include <mbgl/renderer/tile_parameters.hpp> -#include <mbgl/renderer/render_background_layer.hpp> -#include <mbgl/renderer/render_custom_layer.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> -#include <mbgl/renderer/symbol_bucket.hpp> -#include <mbgl/style/style.hpp> +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/query.hpp> +#include <mbgl/text/glyph_atlas.hpp> +#include <mbgl/renderer/image_atlas.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/geometry/feature_index.hpp> #include <mbgl/text/collision_tile.hpp> #include <mbgl/map/transform_state.hpp> -#include <mbgl/map/query.hpp> -#include <mbgl/util/run_loop.hpp> #include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/util/chrono.hpp> #include <mbgl/util/logging.hpp> +#include <mbgl/actor/scheduler.hpp> #include <iostream> @@ -27,32 +27,51 @@ namespace mbgl { using namespace style; +/* + Correlation between GeometryTile and GeometryTileWorker is safeguarded by two + correlation schemes: + + GeometryTile's 'correlationID' is used for ensuring the tile will be flagged + as non-pending only when the placement coming from the last operation (as in + 'setData', 'setLayers', 'setPlacementConfig') occurs. This is important for + still mode rendering as we want to render only when all layout and placement + operations are completed. + + GeometryTileWorker's 'imageCorrelationID' is used for checking whether an + image request reply coming from `GeometryTile` is valid. Previous image + request replies are ignored as they result in incomplete placement attempts + that could flag the tile as non-pending too early. + */ + GeometryTile::GeometryTile(const OverscaledTileID& id_, std::string sourceID_, - const TileParameters& parameters, - GlyphAtlas& glyphAtlas_, - SpriteAtlas& spriteAtlas_) + const TileParameters& parameters) : Tile(id_), sourceID(std::move(sourceID_)), - style(parameters.style), - mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())), + mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())), worker(parameters.workerScheduler, ActorRef<GeometryTile>(*this, mailbox), id_, obsolete, - parameters.mode), - glyphAtlas(glyphAtlas_), - spriteAtlas(spriteAtlas_), - placementThrottler(Milliseconds(300), [this] { invokePlacement(); }) { + parameters.mode, + parameters.pixelRatio), + glyphManager(parameters.glyphManager), + imageManager(parameters.imageManager), + lastYStretch(1.0f), + mode(parameters.mode) { } GeometryTile::~GeometryTile() { - glyphAtlas.removeGlyphs(*this); - spriteAtlas.removeRequestor(*this); - cancel(); + glyphManager.removeRequestor(*this); + imageManager.removeRequestor(*this); + markObsolete(); } void GeometryTile::cancel() { + markObsolete(); +} + +void GeometryTile::markObsolete() { obsolete = true; } @@ -68,7 +87,6 @@ void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) { ++correlationID; worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID); - redoLayout(); } void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) { @@ -82,7 +100,7 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) { ++correlationID; requestedConfig = desiredConfig; - placementThrottler.invoke(); + invokePlacement(); } void GeometryTile::invokePlacement() { @@ -91,29 +109,29 @@ void GeometryTile::invokePlacement() { } } -void GeometryTile::redoLayout() { +void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers) { // Mark the tile as pending again if it was complete before to prevent signaling a complete // state despite pending parse operations. pending = true; - std::vector<std::unique_ptr<Layer>> copy; + std::vector<Immutable<Layer::Impl>> impls; - for (const Layer* layer : style.getLayers()) { - // Avoid cloning and including irrelevant layers. - if (layer->is<BackgroundLayer>() || - layer->is<CustomLayer>() || - layer->baseImpl->source != sourceID || - id.overscaledZ < std::floor(layer->baseImpl->minZoom) || - id.overscaledZ >= std::ceil(layer->baseImpl->maxZoom) || - layer->baseImpl->visibility == VisibilityType::None) { + for (const auto& layer : layers) { + // Skip irrelevant layers. + if (layer->type == LayerType::Background || + layer->type == LayerType::Custom || + layer->source != sourceID || + id.overscaledZ < std::floor(layer->minZoom) || + id.overscaledZ >= std::ceil(layer->maxZoom) || + layer->visibility == VisibilityType::None) { continue; } - copy.push_back(layer->baseImpl->clone()); + impls.push_back(layer); } ++correlationID; - worker.invoke(&GeometryTileWorker::setLayers, std::move(copy), correlationID); + worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID); } void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) { @@ -134,10 +152,16 @@ void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorr pending = false; } symbolBuckets = std::move(result.symbolBuckets); - for (auto& entry : symbolBuckets) { - dynamic_cast<SymbolBucket*>(entry.second.get())->spriteAtlas = &spriteAtlas; - } collisionTile = std::move(result.collisionTile); + if (result.glyphAtlasImage) { + glyphAtlasImage = std::move(*result.glyphAtlasImage); + } + if (result.iconAtlasImage) { + iconAtlasImage = std::move(*result.iconAtlasImage); + } + if (collisionTile.get()) { + lastYStretch = collisionTile->yStretch; + } observer->onTileChanged(*this); } @@ -149,25 +173,51 @@ void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelat observer->onTileError(*this, err); } -void GeometryTile::onGlyphsAvailable(GlyphPositionMap glyphPositions) { - worker.invoke(&GeometryTileWorker::onGlyphsAvailable, std::move(glyphPositions)); +void GeometryTile::onGlyphsAvailable(GlyphMap glyphs) { + worker.invoke(&GeometryTileWorker::onGlyphsAvailable, std::move(glyphs)); } void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) { - glyphAtlas.getGlyphs(*this, std::move(glyphDependencies)); + glyphManager.getGlyphs(*this, std::move(glyphDependencies)); } -void GeometryTile::onIconsAvailable(IconMap icons) { - worker.invoke(&GeometryTileWorker::onIconsAvailable, std::move(icons)); +void GeometryTile::onImagesAvailable(ImageMap images, uint64_t imageCorrelationID) { + worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images), imageCorrelationID); } -void GeometryTile::getIcons(IconDependencies) { - spriteAtlas.getIcons(*this); +void GeometryTile::getImages(ImageRequestPair pair) { + imageManager.getImages(*this, std::move(pair)); } -Bucket* GeometryTile::getBucket(const RenderLayer& layer) const { - const auto& buckets = layer.is<RenderSymbolLayer>() ? symbolBuckets : nonSymbolBuckets; - const auto it = buckets.find(layer.baseImpl.id); +void GeometryTile::upload(gl::Context& context) { + auto uploadFn = [&] (Bucket& bucket) { + if (bucket.needsUpload()) { + bucket.upload(context); + } + }; + + for (auto& entry : nonSymbolBuckets) { + uploadFn(*entry.second); + } + + for (auto& entry : symbolBuckets) { + uploadFn(*entry.second); + } + + if (glyphAtlasImage) { + glyphAtlasTexture = context.createTexture(*glyphAtlasImage, 0); + glyphAtlasImage = {}; + } + + if (iconAtlasImage) { + iconAtlasTexture = context.createTexture(*iconAtlasImage, 0); + iconAtlasImage = {}; + } +} + +Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { + const auto& buckets = layer.type == LayerType::Symbol ? symbolBuckets : nonSymbolBuckets; + const auto it = buckets.find(layer.id); if (it == buckets.end()) { return nullptr; } @@ -180,10 +230,20 @@ void GeometryTile::queryRenderedFeatures( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, const TransformState& transformState, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) { if (!featureIndex || !data) return; + // Determine the additional radius needed factoring in property functions + float additionalRadius = 0; + for (const RenderLayer* layer : layers) { + auto bucket = getBucket(*layer->baseImpl); + if (bucket) { + additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer)); + } + } + featureIndex->query(result, queryGeometry, transformState.getAngle(), @@ -192,9 +252,9 @@ void GeometryTile::queryRenderedFeatures( options, *data, id.canonical, - style, + layers, collisionTile.get(), - *this); + additionalRadius); } void GeometryTile::querySourceFeatures( @@ -233,4 +293,11 @@ void GeometryTile::querySourceFeatures( } } +float GeometryTile::yStretch() const { + // collisionTile gets reset in onLayout but we don't clear the symbolBuckets + // until a new placement result comes along, so keep the yStretch value in + // case we need to render them. + return lastYStretch; +} + } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 910cb446a0..a478aad504 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -1,13 +1,15 @@ #pragma once -#include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/tile/tile.hpp> #include <mbgl/tile/geometry_tile_worker.hpp> -#include <mbgl/text/glyph_atlas.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/text/glyph_manager.hpp> #include <mbgl/text/placement_config.hpp> +#include <mbgl/text/collision_tile.hpp> #include <mbgl/util/feature.hpp> #include <mbgl/util/throttler.hpp> #include <mbgl/actor/actor.hpp> +#include <mbgl/geometry/feature_index.hpp> #include <atomic> #include <memory> @@ -17,23 +19,17 @@ namespace mbgl { class GeometryTileData; -class FeatureIndex; -class CollisionTile; class RenderLayer; class SourceQueryOptions; class TileParameters; +class GlyphAtlas; +class ImageAtlas; -namespace style { -class Style; -} // namespace style - -class GeometryTile : public Tile, public GlyphRequestor, IconRequestor { +class GeometryTile : public Tile, public GlyphRequestor, ImageRequestor { public: GeometryTile(const OverscaledTileID&, std::string sourceID, - const TileParameters&, - GlyphAtlas&, - SpriteAtlas&); + const TileParameters&); ~GeometryTile() override; @@ -41,20 +37,25 @@ public: void setData(std::unique_ptr<const GeometryTileData>); void setPlacementConfig(const PlacementConfig&) override; - void redoLayout() override; + void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) override; - void onGlyphsAvailable(GlyphPositionMap) override; - void onIconsAvailable(IconMap) override; + void onGlyphsAvailable(GlyphMap) override; + void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override; void getGlyphs(GlyphDependencies); - void getIcons(IconDependencies); + void getImages(ImageRequestPair); + + void upload(gl::Context&) override; + Bucket* getBucket(const style::Layer::Impl&) const override; - Bucket* getBucket(const RenderLayer&) const override; + Size bindGlyphAtlas(gl::Context&); + Size bindIconAtlas(gl::Context&); void queryRenderedFeatures( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, const TransformState&, + const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options) override; void querySourceFeatures( @@ -68,6 +69,13 @@ public: std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets; std::unique_ptr<FeatureIndex> featureIndex; std::unique_ptr<GeometryTileData> tileData; + + LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_, + std::unique_ptr<FeatureIndex> featureIndex_, + std::unique_ptr<GeometryTileData> tileData_) + : nonSymbolBuckets(std::move(nonSymbolBuckets_)), + featureIndex(std::move(featureIndex_)), + tileData(std::move(tileData_)) {} }; void onLayout(LayoutResult, uint64_t correlationID); @@ -75,21 +83,34 @@ public: public: std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets; std::unique_ptr<CollisionTile> collisionTile; + optional<AlphaImage> glyphAtlasImage; + optional<PremultipliedImage> iconAtlasImage; + + PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_, + std::unique_ptr<CollisionTile> collisionTile_, + optional<AlphaImage> glyphAtlasImage_, + optional<PremultipliedImage> iconAtlasImage_) + : symbolBuckets(std::move(symbolBuckets_)), + collisionTile(std::move(collisionTile_)), + glyphAtlasImage(std::move(glyphAtlasImage_)), + iconAtlasImage(std::move(iconAtlasImage_)) {} }; void onPlacement(PlacementResult, uint64_t correlationID); void onError(std::exception_ptr, uint64_t correlationID); + float yStretch() const override; + protected: const GeometryTileData* getData() { return data.get(); } private: + void markObsolete(); void invokePlacement(); - + const std::string sourceID; - style::Style& style; // Used to signal the worker that it should abandon parsing this tile as soon as possible. std::atomic<bool> obsolete { false }; @@ -97,8 +118,8 @@ private: std::shared_ptr<Mailbox> mailbox; Actor<GeometryTileWorker> worker; - GlyphAtlas& glyphAtlas; - SpriteAtlas& spriteAtlas; + GlyphManager& glyphManager; + ImageManager& imageManager; uint64_t correlationID = 0; optional<PlacementConfig> requestedConfig; @@ -107,10 +128,18 @@ private: std::unique_ptr<FeatureIndex> featureIndex; std::unique_ptr<const GeometryTileData> data; + optional<AlphaImage> glyphAtlasImage; + optional<PremultipliedImage> iconAtlasImage; + std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets; std::unique_ptr<CollisionTile> collisionTile; - - util::Throttler placementThrottler; + + float lastYStretch; + const MapMode mode; + +public: + optional<gl::Texture> glyphAtlasTexture; + optional<gl::Texture> iconAtlasTexture; }; } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile_data.hpp b/src/mbgl/tile/geometry_tile_data.hpp index 285f86cc7b..449d8cab28 100644 --- a/src/mbgl/tile/geometry_tile_data.hpp +++ b/src/mbgl/tile/geometry_tile_data.hpp @@ -51,7 +51,11 @@ class GeometryTileLayer { public: virtual ~GeometryTileLayer() = default; virtual std::size_t featureCount() const = 0; + + // Returns the feature object at the given position within the layer. The returned feature + // object may *not* outlive the layer object. virtual std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const = 0; + virtual std::string getName() const = 0; }; @@ -59,7 +63,10 @@ class GeometryTileData { public: virtual ~GeometryTileData() = default; virtual std::unique_ptr<GeometryTileData> clone() const = 0; - virtual const GeometryTileLayer* getLayer(const std::string&) const = 0; + + // Returns the layer with the given name. The returned layer object *may* outlive the data + // object. + virtual std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const = 0; }; // classifies an array of rings into polygons with outer rings and holes diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 25834914c4..50429420c3 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -3,14 +3,13 @@ #include <mbgl/tile/geometry_tile.hpp> #include <mbgl/text/collision_tile.hpp> #include <mbgl/layout/symbol_layout.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/renderer/group_by_layout.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/filter_evaluator.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/renderer/render_symbol_layer.hpp> -#include <mbgl/renderer/symbol_bucket.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/string.hpp> @@ -26,16 +25,17 @@ GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_, ActorRef<GeometryTile> parent_, OverscaledTileID id_, const std::atomic<bool>& obsolete_, - const MapMode mode_) + const MapMode mode_, + const float pixelRatio_) : self(std::move(self_)), parent(std::move(parent_)), id(std::move(id_)), obsolete(obsolete_), - mode(mode_) { + mode(mode_), + pixelRatio(pixelRatio_) { } -GeometryTileWorker::~GeometryTileWorker() { -} +GeometryTileWorker::~GeometryTileWorker() = default; /* GeometryTileWorker is a state machine. This is its transition diagram. @@ -92,7 +92,7 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_, } } -void GeometryTileWorker::setLayers(std::vector<std::unique_ptr<Layer>> layers_, uint64_t correlationID_) { +void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_, uint64_t correlationID_) { try { layers = std::move(layers_); correlationID = correlationID_; @@ -144,14 +144,14 @@ void GeometryTileWorker::symbolDependenciesChanged() { try { switch (state) { case Idle: - if (hasPendingSymbolLayouts()) { + if (symbolLayoutsNeedPreparation) { attemptPlacement(); coalesce(); } break; case Coalescing: - if (hasPendingSymbolLayouts()) { + if (symbolLayoutsNeedPreparation) { state = NeedPlacement; } break; @@ -196,37 +196,40 @@ void GeometryTileWorker::coalesce() { self.invoke(&GeometryTileWorker::coalesced); } -void GeometryTileWorker::onGlyphsAvailable(GlyphPositionMap newGlyphPositions) { - for (auto& newFontGlyphs : newGlyphPositions) { +void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) { + for (auto& newFontGlyphs : newGlyphMap) { const FontStack& fontStack = newFontGlyphs.first; - GlyphPositions& newPositions = newFontGlyphs.second; + Glyphs& newGlyphs = newFontGlyphs.second; - GlyphPositions& positions = glyphPositions[fontStack]; + Glyphs& glyphs = glyphMap[fontStack]; GlyphIDs& pendingGlyphIDs = pendingGlyphDependencies[fontStack]; - for (auto& newPosition : newPositions) { - const GlyphID& glyphID = newPosition.first; - optional<Glyph>& glyph = newPosition.second; + for (auto& newGlyph : newGlyphs) { + const GlyphID& glyphID = newGlyph.first; + optional<Immutable<Glyph>>& glyph = newGlyph.second; if (pendingGlyphIDs.erase(glyphID)) { - positions.emplace(glyphID, std::move(glyph)); + glyphs.emplace(glyphID, std::move(glyph)); } } } symbolDependenciesChanged(); } -void GeometryTileWorker::onIconsAvailable(IconMap newIcons) { - icons = std::move(newIcons); - pendingIconDependencies.clear(); +void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap, uint64_t imageCorrelationID_) { + if (imageCorrelationID != imageCorrelationID_) { + return; // Ignore outdated image request replies. + } + imageMap = std::move(newImageMap); + pendingImageDependencies.clear(); symbolDependenciesChanged(); } void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependencies) { for (auto& fontDependencies : glyphDependencies) { - auto fontGlyphs = glyphPositions.find(fontDependencies.first); + auto fontGlyphs = glyphMap.find(fontDependencies.first); for (auto glyphID : fontDependencies.second) { - if (fontGlyphs == glyphPositions.end() || fontGlyphs->second.find(glyphID) == fontGlyphs->second.end()) { + if (fontGlyphs == glyphMap.end() || fontGlyphs->second.find(glyphID) == fontGlyphs->second.end()) { pendingGlyphDependencies[fontDependencies.first].insert(glyphID); } } @@ -236,21 +239,20 @@ void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependen } } -void GeometryTileWorker::requestNewIcons(const IconDependencies& iconDependencies) { - pendingIconDependencies = iconDependencies; - if (!pendingIconDependencies.empty()) { - parent.invoke(&GeometryTile::getIcons, pendingIconDependencies); +void GeometryTileWorker::requestNewImages(const ImageDependencies& imageDependencies) { + pendingImageDependencies = imageDependencies; + if (!pendingImageDependencies.empty()) { + parent.invoke(&GeometryTile::getImages, std::make_pair(pendingImageDependencies, ++imageCorrelationID)); } } -static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vector<std::unique_ptr<style::Layer>>& layers, float zoom) { +static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vector<Immutable<style::Layer::Impl>>& layers, float zoom) { std::vector<std::unique_ptr<RenderLayer>> renderLayers; renderLayers.reserve(layers.size()); for (auto& layer : layers) { - renderLayers.push_back(layer->baseImpl->createRenderLayer()); + renderLayers.push_back(RenderLayer::create(layer)); - renderLayers.back()->cascade(CascadeParameters { - { ClassID::Default }, + renderLayers.back()->transition(TransitionParameters { Clock::time_point::max(), TransitionOptions() }); @@ -269,18 +271,18 @@ void GeometryTileWorker::redoLayout() { std::vector<std::string> symbolOrder; for (auto it = layers->rbegin(); it != layers->rend(); it++) { - if ((*it)->is<SymbolLayer>()) { - symbolOrder.push_back((*it)->getID()); + if ((*it)->type == LayerType::Symbol) { + symbolOrder.push_back((*it)->id); } } std::unordered_map<std::string, std::unique_ptr<SymbolLayout>> symbolLayoutMap; std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; auto featureIndex = std::make_unique<FeatureIndex>(); - BucketParameters parameters { id, mode }; + BucketParameters parameters { id, mode, pixelRatio }; GlyphDependencies glyphDependencies; - IconDependencies iconDependencies; + ImageDependencies imageDependencies; // Create render layers and group by layout std::vector<std::unique_ptr<RenderLayer>> renderLayers = toRenderLayers(*layers, id.overscaledZ); @@ -297,7 +299,7 @@ void GeometryTileWorker::redoLayout() { const RenderLayer& leader = *group.at(0); - auto geometryLayer = (*data)->getLayer(leader.baseImpl.sourceLayer); + auto geometryLayer = (*data)->getLayer(leader.baseImpl->sourceLayer); if (!geometryLayer) { continue; } @@ -310,11 +312,13 @@ void GeometryTileWorker::redoLayout() { featureIndex->setBucketLayerIDs(leader.getID(), layerIDs); if (leader.is<RenderSymbolLayer>()) { - symbolLayoutMap.emplace(leader.getID(), - leader.as<RenderSymbolLayer>()->createLayout(parameters, group, *geometryLayer, glyphDependencies, iconDependencies)); + auto layout = leader.as<RenderSymbolLayer>()->createLayout( + parameters, group, std::move(geometryLayer), glyphDependencies, imageDependencies); + symbolLayoutMap.emplace(leader.getID(), std::move(layout)); + symbolLayoutsNeedPreparation = true; } else { - const Filter& filter = leader.baseImpl.filter; - const std::string& sourceLayerID = leader.baseImpl.sourceLayer; + const Filter& filter = leader.baseImpl->filter; + const std::string& sourceLayerID = leader.baseImpl->sourceLayer; std::shared_ptr<Bucket> bucket = leader.createBucket(parameters, group); for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) { @@ -347,7 +351,7 @@ void GeometryTileWorker::redoLayout() { } requestNewGlyphs(glyphDependencies); - requestNewIcons(iconDependencies); + requestNewImages(imageDependencies); parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult { std::move(buckets), @@ -358,31 +362,42 @@ void GeometryTileWorker::redoLayout() { attemptPlacement(); } -bool GeometryTileWorker::hasPendingSymbolLayouts() const { - for (const auto& symbolLayout : symbolLayouts) { - if (symbolLayout->state == SymbolLayout::Pending) { - return true; - } - } - - return false; -} - bool GeometryTileWorker::hasPendingSymbolDependencies() const { for (auto& glyphDependency : pendingGlyphDependencies) { if (!glyphDependency.second.empty()) { return true; } } - return !pendingIconDependencies.empty(); + return !pendingImageDependencies.empty(); } - void GeometryTileWorker::attemptPlacement() { if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) { return; } + optional<AlphaImage> glyphAtlasImage; + optional<PremultipliedImage> iconAtlasImage; + + if (symbolLayoutsNeedPreparation) { + GlyphAtlas glyphAtlas = makeGlyphAtlas(glyphMap); + ImageAtlas imageAtlas = makeImageAtlas(imageMap); + + glyphAtlasImage = std::move(glyphAtlas.image); + iconAtlasImage = std::move(imageAtlas.image); + + for (auto& symbolLayout : symbolLayouts) { + if (obsolete) { + return; + } + + symbolLayout->prepare(glyphMap, glyphAtlas.positions, + imageMap, imageAtlas.positions); + } + + symbolLayoutsNeedPreparation = false; + } + auto collisionTile = std::make_unique<CollisionTile>(*placementConfig); std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; @@ -391,11 +406,6 @@ void GeometryTileWorker::attemptPlacement() { return; } - if (symbolLayout->state == SymbolLayout::Pending) { - symbolLayout->prepare(glyphPositions, icons); - symbolLayout->state = SymbolLayout::Placed; - } - if (!symbolLayout->hasSymbolInstances()) { continue; } @@ -408,7 +418,9 @@ void GeometryTileWorker::attemptPlacement() { parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult { std::move(buckets), - std::move(collisionTile), + std::move(collisionTile), + std::move(glyphAtlasImage), + std::move(iconAtlasImage), }, correlationID); } diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp index 1df1ef43c4..1425daa7a1 100644 --- a/src/mbgl/tile/geometry_tile_worker.hpp +++ b/src/mbgl/tile/geometry_tile_worker.hpp @@ -2,11 +2,13 @@ #include <mbgl/map/mode.hpp> #include <mbgl/tile/tile_id.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> +#include <mbgl/style/image_impl.hpp> #include <mbgl/text/glyph.hpp> #include <mbgl/text/placement_config.hpp> #include <mbgl/actor/actor_ref.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/style/layer_impl.hpp> #include <atomic> #include <memory> @@ -15,9 +17,7 @@ namespace mbgl { class GeometryTile; class GeometryTileData; -class GlyphAtlas; class SymbolLayout; -class RenderLayer; namespace style { class Layer; @@ -29,15 +29,16 @@ public: ActorRef<GeometryTile> parent, OverscaledTileID, const std::atomic<bool>&, - const MapMode); + const MapMode, + const float pixelRatio); ~GeometryTileWorker(); - void setLayers(std::vector<std::unique_ptr<style::Layer>>, uint64_t correlationID); + void setLayers(std::vector<Immutable<style::Layer::Impl>>, uint64_t correlationID); void setData(std::unique_ptr<const GeometryTileData>, uint64_t correlationID); void setPlacementConfig(PlacementConfig, uint64_t correlationID); - void onGlyphsAvailable(GlyphPositionMap glyphs); - void onIconsAvailable(IconMap icons); + void onGlyphsAvailable(GlyphMap glyphs); + void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID); private: void coalesced(); @@ -47,11 +48,10 @@ private: void coalesce(); void requestNewGlyphs(const GlyphDependencies&); - void requestNewIcons(const IconDependencies&); + void requestNewImages(const ImageDependencies&); void symbolDependenciesChanged(); bool hasPendingSymbolDependencies() const; - bool hasPendingSymbolLayouts() const; ActorRef<GeometryTileWorker> self; ActorRef<GeometryTile> parent; @@ -59,6 +59,7 @@ private: const OverscaledTileID id; const std::atomic<bool>& obsolete; const MapMode mode; + const float pixelRatio; enum State { Idle, @@ -69,17 +70,19 @@ private: State state = Idle; uint64_t correlationID = 0; + uint64_t imageCorrelationID = 0; // Outer optional indicates whether we've received it or not. - optional<std::vector<std::unique_ptr<style::Layer>>> layers; + optional<std::vector<Immutable<style::Layer::Impl>>> layers; optional<std::unique_ptr<const GeometryTileData>> data; optional<PlacementConfig> placementConfig; + bool symbolLayoutsNeedPreparation = false; std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts; GlyphDependencies pendingGlyphDependencies; - IconDependencies pendingIconDependencies; - GlyphPositionMap glyphPositions; - IconMap icons; + ImageDependencies pendingImageDependencies; + GlyphMap glyphMap; + ImageMap imageMap; }; } // namespace mbgl diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index 12dfaf87d4..2a3c9eeb0e 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -7,8 +7,8 @@ #include <mbgl/storage/response.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/renderer/tile_parameters.hpp> -#include <mbgl/renderer/raster_bucket.hpp> -#include <mbgl/util/run_loop.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/actor/scheduler.hpp> namespace mbgl { @@ -17,7 +17,7 @@ RasterTile::RasterTile(const OverscaledTileID& id_, const Tileset& tileset) : Tile(id_), loader(*this, id_, parameters, tileset), - mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())), + mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())), worker(parameters.workerScheduler, ActorRef<RasterTile>(*this, mailbox)) { } @@ -43,7 +43,7 @@ void RasterTile::setData(std::shared_ptr<const std::string> data, worker.invoke(&RasterTileWorker::parse, data, correlationID); } -void RasterTile::onParsed(std::unique_ptr<Bucket> result, const uint64_t resultCorrelationID) { +void RasterTile::onParsed(std::unique_ptr<RasterBucket> result, const uint64_t resultCorrelationID) { bucket = std::move(result); loaded = true; if (resultCorrelationID == correlationID) { @@ -61,10 +61,22 @@ void RasterTile::onError(std::exception_ptr err, const uint64_t resultCorrelatio observer->onTileError(*this, err); } -Bucket* RasterTile::getBucket(const RenderLayer&) const { +void RasterTile::upload(gl::Context& context) { + if (bucket) { + bucket->upload(context); + } +} + +Bucket* RasterTile::getBucket(const style::Layer::Impl&) const { return bucket.get(); } +void RasterTile::setMask(TileMask&& mask) { + if (bucket) { + bucket->setMask(std::move(mask)); + } +} + void RasterTile::setNecessity(Necessity necessity) { loader.setNecessity(necessity); } diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp index 28fcb554a9..2cb64e8ed7 100644 --- a/src/mbgl/tile/raster_tile.hpp +++ b/src/mbgl/tile/raster_tile.hpp @@ -9,6 +9,7 @@ namespace mbgl { class Tileset; class TileParameters; +class RasterBucket; namespace style { class Layer; @@ -29,9 +30,13 @@ public: optional<Timestamp> expires_); void cancel() override; - Bucket* getBucket(const RenderLayer&) const override; - void onParsed(std::unique_ptr<Bucket> result, uint64_t correlationID); + void upload(gl::Context&) override; + Bucket* getBucket(const style::Layer::Impl&) const override; + + void setMask(TileMask&&) override; + + void onParsed(std::unique_ptr<RasterBucket> result, uint64_t correlationID); void onError(std::exception_ptr, uint64_t correlationID); private: @@ -44,7 +49,7 @@ private: // Contains the Bucket object for the tile. Buckets are render // objects and they get added by tile parsing operations. - std::unique_ptr<Bucket> bucket; + std::unique_ptr<RasterBucket> bucket; }; } // namespace mbgl diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp index 585fe5ec95..4afa876429 100644 --- a/src/mbgl/tile/raster_tile_worker.cpp +++ b/src/mbgl/tile/raster_tile_worker.cpp @@ -1,6 +1,6 @@ #include <mbgl/tile/raster_tile_worker.hpp> #include <mbgl/tile/raster_tile.hpp> -#include <mbgl/renderer/raster_bucket.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> #include <mbgl/actor/actor.hpp> #include <mbgl/util/premultiply.hpp> @@ -17,7 +17,7 @@ void RasterTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t c } try { - auto bucket = std::make_unique<RasterBucket>(util::unpremultiply(decodeImage(*data))); + auto bucket = std::make_unique<RasterBucket>(decodeImage(*data)); parent.invoke(&RasterTile::onParsed, std::move(bucket), correlationID); } catch (...) { parent.invoke(&RasterTile::onError, std::current_exception(), correlationID); diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index 0adc151a64..7d7eb0b3fc 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -1,9 +1,9 @@ #include <mbgl/tile/tile.hpp> #include <mbgl/tile/tile_observer.hpp> -#include <mbgl/renderer/debug_bucket.hpp> +#include <mbgl/renderer/buckets/debug_bucket.hpp> +#include <mbgl/renderer/query.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/logging.hpp> -#include <mbgl/map/query.hpp> namespace mbgl { @@ -33,6 +33,7 @@ void Tile::queryRenderedFeatures( std::unordered_map<std::string, std::vector<Feature>>&, const GeometryCoordinates&, const TransformState&, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions&) {} void Tile::querySourceFeatures( diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 795fd62140..39cc0de8bd 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -6,9 +6,11 @@ #include <mbgl/util/feature.hpp> #include <mbgl/util/tile_coordinate.hpp> #include <mbgl/tile/tile_id.hpp> +#include <mbgl/renderer/tile_mask.hpp> #include <mbgl/renderer/bucket.hpp> #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/storage/resource.hpp> +#include <mbgl/style/layer_impl.hpp> #include <string> #include <memory> @@ -21,12 +23,13 @@ class DebugBucket; class TransformState; class TileObserver; class PlacementConfig; +class RenderLayer; class RenderedQueryOptions; class SourceQueryOptions; -class RenderLayer; -namespace style { -} // namespace style +namespace gl { +class Context; +} // namespace gl class Tile : private util::noncopyable { public: @@ -47,15 +50,18 @@ public: // Mark this tile as no longer needed and cancel any pending work. virtual void cancel() = 0; - virtual Bucket* getBucket(const RenderLayer&) const = 0; + virtual void upload(gl::Context&) = 0; + virtual Bucket* getBucket(const style::Layer::Impl&) const = 0; virtual void setPlacementConfig(const PlacementConfig&) {} - virtual void redoLayout() {} + virtual void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) {} + virtual void setMask(TileMask&&) {} virtual void queryRenderedFeatures( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, const TransformState&, + const std::vector<const RenderLayer*>&, const RenderedQueryOptions& options); virtual void querySourceFeatures( @@ -101,6 +107,8 @@ public: // Contains the tile ID string for painting debug information. std::unique_ptr<DebugBucket> debugBucket; + + virtual float yStretch() const { return 1.0f; } protected: bool triedOptional = false; diff --git a/src/mbgl/tile/tile_id.hpp b/src/mbgl/tile/tile_id.hpp deleted file mode 100644 index 1ce3eea98e..0000000000 --- a/src/mbgl/tile/tile_id.hpp +++ /dev/null @@ -1,275 +0,0 @@ -#pragma once - -#include <mbgl/util/constants.hpp> - -#include <cstdint> -#include <array> -#include <forward_list> -#include <algorithm> -#include <iosfwd> -#include <cassert> -#include <boost/functional/hash.hpp> - -namespace mbgl { - -class OverscaledTileID; -class CanonicalTileID; -class UnwrappedTileID; - -// Has integer z/x/y coordinates -// All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid) -// Used for requesting data; represents data tiles that exist out there. -// z is never larger than the source's maxzoom -class CanonicalTileID { -public: - CanonicalTileID(uint8_t z, uint32_t x, uint32_t y); - bool operator==(const CanonicalTileID&) const; - bool operator!=(const CanonicalTileID&) const; - bool operator<(const CanonicalTileID&) const; - bool isChildOf(const CanonicalTileID&) const; - CanonicalTileID scaledTo(uint8_t z) const; - std::array<CanonicalTileID, 4> children() const; - - const uint8_t z; - const uint32_t x; - const uint32_t y; -}; - -::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs); -namespace util { -std::string toString(const CanonicalTileID&); -} // namespace util - -// Has integer z/x/y coordinates -// overscaledZ describes the zoom level this tile is intented to represent, e.g. when parsing data -// z is never larger than the source's maxzoom -// z/x/y describe the -class OverscaledTileID { -public: - OverscaledTileID(uint8_t overscaledZ, CanonicalTileID); - OverscaledTileID(uint8_t overscaledZ, uint8_t z, uint32_t x, uint32_t y); - OverscaledTileID(uint8_t z, uint32_t x, uint32_t y); - explicit OverscaledTileID(const CanonicalTileID&); - explicit OverscaledTileID(CanonicalTileID&&); - bool operator==(const OverscaledTileID&) const; - bool operator!=(const OverscaledTileID&) const; - bool operator<(const OverscaledTileID&) const; - bool isChildOf(const OverscaledTileID&) const; - uint32_t overscaleFactor() const; - OverscaledTileID scaledTo(uint8_t z) const; - UnwrappedTileID unwrapTo(int16_t wrap) const; - - const uint8_t overscaledZ; - const CanonicalTileID canonical; -}; - -::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs); -namespace util { -std::string toString(const OverscaledTileID&); -} // namespace util - -// Has integer z/x/y coordinates -// wrap describes tiles that are left/right of the main tile pyramid, e.g. when wrapping the world -// Used for describing what position tiles are getting rendered at (= calc the matrix) -// z is never larger than the source's maxzoom -class UnwrappedTileID { -public: - UnwrappedTileID(uint8_t z, int64_t x, int64_t y); - UnwrappedTileID(int16_t wrap, CanonicalTileID); - bool operator==(const UnwrappedTileID&) const; - bool operator!=(const UnwrappedTileID&) const; - bool operator<(const UnwrappedTileID&) const; - bool isChildOf(const UnwrappedTileID&) const; - std::array<UnwrappedTileID, 4> children() const; - OverscaledTileID overscaleTo(uint8_t z) const; - float pixelsToTileUnits(float pixelValue, float zoom) const; - - const int16_t wrap; - const CanonicalTileID canonical; -}; - -::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs); -namespace util { -std::string toString(const UnwrappedTileID&); -} // namespace util - -inline CanonicalTileID::CanonicalTileID(uint8_t z_, uint32_t x_, uint32_t y_) : z(z_), x(x_), y(y_) { - assert(z <= 32); - assert(x < (1ull << z)); - assert(y < (1ull << z)); -} - -inline bool CanonicalTileID::operator==(const CanonicalTileID& rhs) const { - return z == rhs.z && x == rhs.x && y == rhs.y; -} - -inline bool CanonicalTileID::operator!=(const CanonicalTileID& rhs) const { - return z != rhs.z || x != rhs.x || y != rhs.y; -} - -inline bool CanonicalTileID::operator<(const CanonicalTileID& rhs) const { - return std::tie(z, x, y) < std::tie(rhs.z, rhs.x, rhs.y); -} - -inline bool CanonicalTileID::isChildOf(const CanonicalTileID& parent) const { - // We're first testing for z == 0, to avoid a 32 bit shift, which is undefined. - return parent.z == 0 || - (parent.z < z && parent.x == (x >> (z - parent.z)) && parent.y == (y >> (z - parent.z))); -} - -inline CanonicalTileID CanonicalTileID::scaledTo(uint8_t targetZ) const { - if (targetZ <= z) { - return { targetZ, x >> (z - targetZ), y >> (z - targetZ) }; // parent or same - } else { - return { targetZ, x << (targetZ - z), y << (targetZ - z) }; // child - } -} - -inline std::array<CanonicalTileID, 4> CanonicalTileID::children() const { - const uint8_t childZ = z + 1; - const uint32_t childX = x * 2; - const uint32_t childY = y * 2; - return { { - { childZ, childX, childY }, - { childZ, childX, childY + 1 }, - { childZ, childX + 1, childY }, - { childZ, childX + 1, childY + 1 }, - } }; -} - -inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, CanonicalTileID canonical_) - : overscaledZ(overscaledZ_), canonical(std::move(canonical_)) { - assert(overscaledZ >= canonical.z); -} - -inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, uint8_t z, uint32_t x, uint32_t y) - : overscaledZ(overscaledZ_), canonical(z, x, y) { - assert(overscaledZ >= canonical.z); -} - -inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y) - : overscaledZ(z), canonical(z, x, y) { -} - -inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_) - : overscaledZ(canonical_.z), canonical(canonical_) { - assert(overscaledZ >= canonical.z); -} - -inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_) - : overscaledZ(canonical_.z), canonical(std::forward<CanonicalTileID>(canonical_)) { - assert(overscaledZ >= canonical.z); -} - -inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const { - return overscaledZ == rhs.overscaledZ && canonical == rhs.canonical; -} - -inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const { - return overscaledZ != rhs.overscaledZ || canonical != rhs.canonical; -} - -inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const { - return std::tie(overscaledZ, canonical) < std::tie(rhs.overscaledZ, rhs.canonical); -} - -inline uint32_t OverscaledTileID::overscaleFactor() const { - return 1u << (overscaledZ - canonical.z); -} - -inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const { - return overscaledZ > rhs.overscaledZ && - (canonical == rhs.canonical || canonical.isChildOf(rhs.canonical)); -} - -inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const { - return { z, z >= canonical.z ? canonical : canonical.scaledTo(z) }; -} - -inline UnwrappedTileID OverscaledTileID::unwrapTo(int16_t wrap) const { - return { wrap, canonical }; -} - -inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_) - : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)), - canonical( - z_, - static_cast<uint32_t>(x_ - wrap * (1ll << z_)), - y_ < 0 ? 0 : std::min(static_cast<uint32_t>(y_), static_cast<uint32_t>(1ull << z_) - 1)) { -} - -inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_) - : wrap(wrap_), canonical(std::move(canonical_)) { -} - -inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const { - return wrap == rhs.wrap && canonical == rhs.canonical; -} - -inline bool UnwrappedTileID::operator!=(const UnwrappedTileID& rhs) const { - return wrap != rhs.wrap || canonical != rhs.canonical; -} - -inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const { - return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical); -} - -inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const { - return wrap == parent.wrap && canonical.isChildOf(parent.canonical); -} - -inline std::array<UnwrappedTileID, 4> UnwrappedTileID::children() const { - const uint8_t childZ = canonical.z + 1; - const uint32_t childX = canonical.x * 2; - const uint32_t childY = canonical.y * 2; - return { { - { wrap, { childZ, childX, childY } }, - { wrap, { childZ, childX, childY + 1 } }, - { wrap, { childZ, childX + 1, childY } }, - { wrap, { childZ, childX + 1, childY + 1 } }, - } }; -} - -inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const { - assert(overscaledZ >= canonical.z); - return { overscaledZ, canonical }; -} - -inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const { - return pixelValue * (util::EXTENT / (util::tileSize * std::pow(2, zoom - canonical.z))); -} - -} // namespace mbgl - -namespace std { - -template <> struct hash<mbgl::CanonicalTileID> { - size_t operator()(const mbgl::CanonicalTileID &id) const { - std::size_t seed = 0; - boost::hash_combine(seed, id.x); - boost::hash_combine(seed, id.y); - boost::hash_combine(seed, id.z); - return seed; - } -}; - -template <> struct hash<mbgl::UnwrappedTileID> { - size_t operator()(const mbgl::UnwrappedTileID &id) const { - std::size_t seed = 0; - boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical)); - boost::hash_combine(seed, id.wrap); - return seed; - } -}; - -template <> struct hash<mbgl::OverscaledTileID> { - size_t operator()(const mbgl::OverscaledTileID &id) const { - std::size_t seed = 0; - boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical)); - boost::hash_combine(seed, id.overscaledZ); - return seed; - } -}; - -} // namespace std - diff --git a/src/mbgl/tile/tile_id_hash.cpp b/src/mbgl/tile/tile_id_hash.cpp new file mode 100644 index 0000000000..4a1f185817 --- /dev/null +++ b/src/mbgl/tile/tile_id_hash.cpp @@ -0,0 +1,29 @@ +#include <mbgl/tile/tile_id.hpp> + +#include <boost/functional/hash.hpp> + +namespace std { + +size_t hash<mbgl::CanonicalTileID>::operator()(const mbgl::CanonicalTileID& id) const { + std::size_t seed = 0; + boost::hash_combine(seed, id.x); + boost::hash_combine(seed, id.y); + boost::hash_combine(seed, id.z); + return seed; +} + +size_t hash<mbgl::UnwrappedTileID>::operator()(const mbgl::UnwrappedTileID& id) const { + std::size_t seed = 0; + boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical)); + boost::hash_combine(seed, id.wrap); + return seed; +} + +size_t hash<mbgl::OverscaledTileID>::operator()(const mbgl::OverscaledTileID& id) const { + std::size_t seed = 0; + boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical)); + boost::hash_combine(seed, id.overscaledZ); + return seed; +} + +} // namespace std diff --git a/src/mbgl/tile/tile_id_io.cpp b/src/mbgl/tile/tile_id_io.cpp index f6adbf183f..d8be6b93d6 100644 --- a/src/mbgl/tile/tile_id_io.cpp +++ b/src/mbgl/tile/tile_id_io.cpp @@ -6,6 +6,8 @@ namespace mbgl { ::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs) { + // Uncomment this to create code instead of shorthands. + // return os << "CanonicalTileID{ " << uint32_t(rhs.z) << ", " << rhs.x << ", " << rhs.y << " }"; return os << uint32_t(rhs.z) << "/" << rhs.x << "/" << rhs.y; } @@ -26,6 +28,9 @@ std::string toString(const OverscaledTileID& rhs) { } // namespace util ::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs) { + // Uncomment this to create code instead of shorthands. + // return os << "UnwrappedTileID{ " << uint32_t(rhs.wrap) << ", { " << uint32_t(rhs.canonical.z) + // << ", " << rhs.canonical.x << ", " << rhs.canonical.y << " } }"; return os << rhs.canonical << (rhs.wrap >= 0 ? "+" : "") << rhs.wrap; } diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp index 899cbaf9b0..598ec32c10 100644 --- a/src/mbgl/tile/tile_loader_impl.hpp +++ b/src/mbgl/tile/tile_loader_impl.hpp @@ -61,7 +61,10 @@ void TileLoader<T>::loadOptional() { // When the optional request could not be satisfied, don't treat it as an error. // Instead, we make sure that the next request knows that there has been an optional // request before by setting one of the prior* fields. + resource.priorModified = res.modified; resource.priorExpires = Timestamp{ Seconds::zero() }; + resource.priorEtag = res.etag; + resource.priorData = res.data; } else { loadedData(res); } diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index 46914e5f5a..e2e700b7b7 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -1,94 +1,15 @@ #include <mbgl/tile/vector_tile.hpp> +#include <mbgl/tile/vector_tile_data.hpp> #include <mbgl/tile/tile_loader_impl.hpp> -#include <mbgl/tile/geometry_tile_data.hpp> -#include <mbgl/style/style.hpp> #include <mbgl/renderer/tile_parameters.hpp> -#include <protozero/pbf_reader.hpp> - -#include <unordered_map> -#include <functional> -#include <utility> - namespace mbgl { -class VectorTileLayer; - -using packed_iter_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>; - -struct VectorTileLayerData { - VectorTileLayerData(std::shared_ptr<const std::string>); - - // Hold a reference to the underlying pbf data that backs the lazily-built - // components of the owning VectorTileLayer and VectorTileFeature objects - std::shared_ptr<const std::string> data; - - uint32_t version = 1; - uint32_t extent = 4096; - std::unordered_map<std::string, uint32_t> keysMap; - std::vector<std::reference_wrapper<const std::string>> keys; - std::vector<Value> values; -}; - -class VectorTileFeature : public GeometryTileFeature { -public: - VectorTileFeature(protozero::pbf_reader, std::shared_ptr<VectorTileLayerData> layerData); - - FeatureType getType() const override { return type; } - optional<Value> getValue(const std::string&) const override; - std::unordered_map<std::string,Value> getProperties() const override; - optional<FeatureIdentifier> getID() const override; - GeometryCollection getGeometries() const override; - -private: - std::shared_ptr<VectorTileLayerData> layerData; - optional<FeatureIdentifier> id; - FeatureType type = FeatureType::Unknown; - packed_iter_type tags_iter; - packed_iter_type geometry_iter; -}; - -class VectorTileLayer : public GeometryTileLayer { -public: - VectorTileLayer(protozero::pbf_reader, std::shared_ptr<const std::string>); - - std::size_t featureCount() const override { return features.size(); } - std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const override; - std::string getName() const override; - -private: - friend class VectorTileData; - friend class VectorTileFeature; - - std::string name; - std::vector<protozero::pbf_reader> features; - std::shared_ptr<VectorTileLayerData> data; -}; - -class VectorTileData : public GeometryTileData { -public: - VectorTileData(std::shared_ptr<const std::string> data); - - std::unique_ptr<GeometryTileData> clone() const override { - return std::make_unique<VectorTileData>(*this); - } - - const GeometryTileLayer* getLayer(const std::string&) const override; - -private: - std::shared_ptr<const std::string> data; - mutable bool parsed = false; - mutable std::unordered_map<std::string, VectorTileLayer> layers; -}; - VectorTile::VectorTile(const OverscaledTileID& id_, std::string sourceID_, const TileParameters& parameters, const Tileset& tileset) - : GeometryTile(id_, sourceID_, parameters, - *parameters.style.glyphAtlas, - *parameters.style.spriteAtlas), - loader(*this, id_, parameters, tileset) { + : GeometryTile(id_, sourceID_, parameters), loader(*this, id_, parameters, tileset) { } void VectorTile::setNecessity(Necessity necessity) { @@ -104,220 +25,4 @@ void VectorTile::setData(std::shared_ptr<const std::string> data_, GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(data_) : nullptr); } -Value parseValue(protozero::pbf_reader data) { - while (data.next()) - { - switch (data.tag()) { - case 1: // string_value - return data.get_string(); - case 2: // float_value - return static_cast<double>(data.get_float()); - case 3: // double_value - return data.get_double(); - case 4: // int_value - return data.get_int64(); - case 5: // uint_value - return data.get_uint64(); - case 6: // sint_value - return data.get_sint64(); - case 7: // bool_value - return data.get_bool(); - default: - data.skip(); - break; - } - } - return false; -} - -VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, std::shared_ptr<VectorTileLayerData> layerData_) - : layerData(std::move(layerData_)) { - while (feature_pbf.next()) { - switch (feature_pbf.tag()) { - case 1: // id - id = { feature_pbf.get_uint64() }; - break; - case 2: // tags - tags_iter = feature_pbf.get_packed_uint32(); - break; - case 3: // type - type = static_cast<FeatureType>(feature_pbf.get_enum()); - break; - case 4: // geometry - geometry_iter = feature_pbf.get_packed_uint32(); - break; - default: - feature_pbf.skip(); - break; - } - } -} - -optional<Value> VectorTileFeature::getValue(const std::string& key) const { - auto keyIter = layerData->keysMap.find(key); - if (keyIter == layerData->keysMap.end()) { - return optional<Value>(); - } - - auto start_itr = tags_iter.begin(); - const auto & end_itr = tags_iter.end(); - while (start_itr != end_itr) { - uint32_t tag_key = static_cast<uint32_t>(*start_itr++); - - if (layerData->keysMap.size() <= tag_key) { - throw std::runtime_error("feature referenced out of range key"); - } - - if (start_itr == end_itr) { - throw std::runtime_error("uneven number of feature tag ids"); - } - - uint32_t tag_val = static_cast<uint32_t>(*start_itr++);; - if (layerData->values.size() <= tag_val) { - throw std::runtime_error("feature referenced out of range value"); - } - - if (tag_key == keyIter->second) { - return layerData->values[tag_val]; - } - } - - return optional<Value>(); -} - -std::unordered_map<std::string,Value> VectorTileFeature::getProperties() const { - std::unordered_map<std::string,Value> properties; - auto start_itr = tags_iter.begin(); - const auto & end_itr = tags_iter.end(); - while (start_itr != end_itr) { - uint32_t tag_key = static_cast<uint32_t>(*start_itr++); - if (start_itr == end_itr) { - throw std::runtime_error("uneven number of feature tag ids"); - } - uint32_t tag_val = static_cast<uint32_t>(*start_itr++); - properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val); - } - return properties; -} - -optional<FeatureIdentifier> VectorTileFeature::getID() const { - return id; -} - -GeometryCollection VectorTileFeature::getGeometries() const { - uint8_t cmd = 1; - uint32_t length = 0; - int32_t x = 0; - int32_t y = 0; - const float scale = float(util::EXTENT) / layerData->extent; - - GeometryCollection lines; - - lines.emplace_back(); - GeometryCoordinates* line = &lines.back(); - - auto g_itr = geometry_iter.begin(); - while (g_itr != geometry_iter.end()) { - if (length == 0) { - uint32_t cmd_length = static_cast<uint32_t>(*g_itr++); - cmd = cmd_length & 0x7; - length = cmd_length >> 3; - } - - --length; - - if (cmd == 1 || cmd == 2) { - x += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++)); - y += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++)); - - if (cmd == 1 && !line->empty()) { // moveTo - lines.emplace_back(); - line = &lines.back(); - } - - line->emplace_back(::round(x * scale), ::round(y * scale)); - - } else if (cmd == 7) { // closePolygon - if (!line->empty()) { - line->push_back((*line)[0]); - } - - } else { - throw std::runtime_error("unknown command"); - } - } - - if (layerData->version >= 2 || type != FeatureType::Polygon) { - return lines; - } - - return fixupPolygons(lines); -} - -VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_) - : data(std::move(data_)) { -} - -const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const { - if (!parsed) { - parsed = true; - protozero::pbf_reader tile_pbf(*data); - while (tile_pbf.next(3)) { - VectorTileLayer layer(tile_pbf.get_message(), data); - layers.emplace(layer.name, std::move(layer)); - } - } - - auto it = layers.find(name); - if (it != layers.end()) { - return &it->second; - } - return nullptr; -} - -VectorTileLayerData::VectorTileLayerData(std::shared_ptr<const std::string> pbfData) : - data(std::move(pbfData)) -{} - -VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr<const std::string> pbfData) - : data(std::make_shared<VectorTileLayerData>(std::move(pbfData))) -{ - while (layer_pbf.next()) { - switch (layer_pbf.tag()) { - case 1: // name - name = layer_pbf.get_string(); - break; - case 2: // feature - features.push_back(layer_pbf.get_message()); - break; - case 3: // keys - { - auto iter = data->keysMap.emplace(layer_pbf.get_string(), data->keysMap.size()); - data->keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first)); - } - break; - case 4: // values - data->values.emplace_back(parseValue(layer_pbf.get_message())); - break; - case 5: // extent - data->extent = layer_pbf.get_uint32(); - break; - case 15: // version - data->version = layer_pbf.get_uint32(); - break; - default: - layer_pbf.skip(); - break; - } - } -} - -std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const { - return std::make_unique<VectorTileFeature>(features.at(i), data); -} - -std::string VectorTileLayer::getName() const { - return name; -} - } // namespace mbgl diff --git a/src/mbgl/tile/vector_tile_data.cpp b/src/mbgl/tile/vector_tile_data.cpp new file mode 100644 index 0000000000..2d4a01bda3 --- /dev/null +++ b/src/mbgl/tile/vector_tile_data.cpp @@ -0,0 +1,89 @@ +#include <mbgl/tile/vector_tile_data.hpp> +#include <mbgl/util/constants.hpp> + +namespace mbgl { + +VectorTileFeature::VectorTileFeature(const mapbox::vector_tile::layer& layer, + const protozero::data_view& view) + : feature(view, layer) { +} + +FeatureType VectorTileFeature::getType() const { + switch (feature.getType()) { + case mapbox::vector_tile::GeomType::POINT: + return FeatureType::Point; + case mapbox::vector_tile::GeomType::LINESTRING: + return FeatureType::LineString; + case mapbox::vector_tile::GeomType::POLYGON: + return FeatureType::Polygon; + default: + return FeatureType::Unknown; + } +} + +optional<Value> VectorTileFeature::getValue(const std::string& key) const { + return feature.getValue(key); +} + +std::unordered_map<std::string, Value> VectorTileFeature::getProperties() const { + return feature.getProperties(); +} + +optional<FeatureIdentifier> VectorTileFeature::getID() const { + return feature.getID(); +} + +GeometryCollection VectorTileFeature::getGeometries() const { + const float scale = float(util::EXTENT) / feature.getExtent(); + auto lines = feature.getGeometries<GeometryCollection>(scale); + if (feature.getVersion() >= 2 || feature.getType() != mapbox::vector_tile::GeomType::POLYGON) { + return lines; + } else { + return fixupPolygons(lines); + } +} + +VectorTileLayer::VectorTileLayer(std::shared_ptr<const std::string> data_, + const protozero::data_view& view) + : data(std::move(data_)), layer(view) { +} + +std::size_t VectorTileLayer::featureCount() const { + return layer.featureCount(); +} + +std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const { + return std::make_unique<VectorTileFeature>(layer, layer.getFeature(i)); +} + +std::string VectorTileLayer::getName() const { + return layer.getName(); +} + +VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_) : data(std::move(data_)) { +} + +std::unique_ptr<GeometryTileData> VectorTileData::clone() const { + return std::make_unique<VectorTileData>(data); +} + +std::unique_ptr<GeometryTileLayer> VectorTileData::getLayer(const std::string& name) const { + if (!parsed) { + // We're parsing this lazily so that we can construct VectorTileData objects on the main + // thread without incurring the overhead of parsing immediately. + layers = mapbox::vector_tile::buffer(*data).getLayers(); + parsed = true; + } + + auto it = layers.find(name); + if (it != layers.end()) { + return std::make_unique<VectorTileLayer>(data, it->second); + } + return nullptr; +} + +std::vector<std::string> VectorTileData::layerNames() const { + return mapbox::vector_tile::buffer(*data).layerNames(); +} + +} // namespace mbgl diff --git a/src/mbgl/tile/vector_tile_data.hpp b/src/mbgl/tile/vector_tile_data.hpp new file mode 100644 index 0000000000..48beaf9d07 --- /dev/null +++ b/src/mbgl/tile/vector_tile_data.hpp @@ -0,0 +1,54 @@ +#include <mbgl/tile/geometry_tile_data.hpp> + +#include <mapbox/vector_tile.hpp> +#include <protozero/pbf_reader.hpp> + +#include <unordered_map> +#include <functional> +#include <utility> + +namespace mbgl { + +class VectorTileFeature : public GeometryTileFeature { +public: + VectorTileFeature(const mapbox::vector_tile::layer&, const protozero::data_view&); + + FeatureType getType() const override; + optional<Value> getValue(const std::string& key) const override; + std::unordered_map<std::string, Value> getProperties() const override; + optional<FeatureIdentifier> getID() const override; + GeometryCollection getGeometries() const override; + +private: + mapbox::vector_tile::feature feature; +}; + +class VectorTileLayer : public GeometryTileLayer { +public: + VectorTileLayer(std::shared_ptr<const std::string> data, const protozero::data_view&); + + std::size_t featureCount() const override; + std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override; + std::string getName() const override; + +private: + std::shared_ptr<const std::string> data; + mapbox::vector_tile::layer layer; +}; + +class VectorTileData : public GeometryTileData { +public: + VectorTileData(std::shared_ptr<const std::string> data); + + std::unique_ptr<GeometryTileData> clone() const override; + std::unique_ptr<GeometryTileLayer> getLayer(const std::string& name) const override; + + std::vector<std::string> layerNames() const; + +private: + std::shared_ptr<const std::string> data; + mutable bool parsed = false; + mutable std::map<std::string, const protozero::data_view> layers; +}; + +} // namespace mbgl diff --git a/src/mbgl/util/chrono.cpp b/src/mbgl/util/chrono.cpp index 5c8fd3c0ff..a880093b74 100644 --- a/src/mbgl/util/chrono.cpp +++ b/src/mbgl/util/chrono.cpp @@ -3,6 +3,13 @@ #include <parsedate/parsedate.h> #include <cstdio> +#include <ctime> + +#if defined(_WINDOWS) +#define _gmtime(t, i) gmtime_s(i, t) +#else +#define _gmtime(t, i) gmtime_r(t, i) +#endif namespace mbgl { namespace util { @@ -14,7 +21,7 @@ static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", std::string rfc1123(Timestamp timestamp) { std::time_t time = std::chrono::system_clock::to_time_t(timestamp); std::tm info; - gmtime_r(&time, &info); + _gmtime(&time, &info); char buffer[30]; snprintf(buffer, 30, "%s, %02d %s %4d %02d:%02d:%02d GMT", week[info.tm_wday], info.tm_mday, months[info.tm_mon], 1900 + info.tm_year, info.tm_hour, info.tm_min, info.tm_sec); @@ -24,7 +31,7 @@ std::string rfc1123(Timestamp timestamp) { std::string iso8601(Timestamp timestamp) { std::time_t time = std::chrono::system_clock::to_time_t(timestamp); std::tm info; - gmtime_r(&time, &info); + _gmtime(&time, &info); char buffer[30]; std::strftime(buffer, sizeof(buffer), "%F %T", &info); return buffer; diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp index 9faef140ef..56f78c9885 100644 --- a/src/mbgl/util/constants.cpp +++ b/src/mbgl/util/constants.cpp @@ -11,7 +11,6 @@ const bool tileParseWarnings = false; const bool styleParseWarnings = false; const bool spriteWarnings = false; const bool renderWarnings = false; -const bool renderTree = false; const bool labelTextMissingWarning = true; const bool missingFontStackWarning = true; const bool missingFontFaceWarning = true; @@ -22,7 +21,6 @@ const bool tileParseWarnings = false; const bool styleParseWarnings = false; const bool spriteWarnings = false; const bool renderWarnings = false; -const bool renderTree = false; const bool labelTextMissingWarning = false; const bool missingFontStackWarning = false; const bool missingFontFaceWarning = false; diff --git a/src/mbgl/util/dtoa.cpp b/src/mbgl/util/dtoa.cpp index dd4fba0f89..0e3aef6117 100644 --- a/src/mbgl/util/dtoa.cpp +++ b/src/mbgl/util/dtoa.cpp @@ -1,10 +1,18 @@ #include "dtoa.hpp" +// Clang/C2 on Windows 64-bits can't parse rapidjson's dtoa +// and it was causing the compiler to crash. +#if !defined(_WINDOWS) #include <rapidjson/internal/dtoa.h> +#endif + +#include <mbgl/util/string.hpp> namespace mbgl { namespace util { +#if !defined(_WINDOWS) + namespace { // From https://github.com/miloyip/rapidjson/blob/master/include/rapidjson/internal/dtoa.h @@ -101,5 +109,13 @@ std::string dtoa(double value) { return data; } +#else + +std::string dtoa(double value) { + return std::to_string(value); +} + +#endif + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/dtoa.hpp b/src/mbgl/util/dtoa.hpp index db7d309452..4cb81a94be 100644 --- a/src/mbgl/util/dtoa.hpp +++ b/src/mbgl/util/dtoa.hpp @@ -5,7 +5,6 @@ namespace mbgl { namespace util { -char* dtoa(double value, char* buffer); std::string dtoa(double value); } // end namespace util diff --git a/src/mbgl/util/geojson.cpp b/src/mbgl/util/geojson_impl.cpp index d1608e09fe..d1608e09fe 100644 --- a/src/mbgl/util/geojson.cpp +++ b/src/mbgl/util/geojson_impl.cpp diff --git a/src/mbgl/util/http_header.cpp b/src/mbgl/util/http_header.cpp index 40711232ff..ce31a06c5e 100644 --- a/src/mbgl/util/http_header.cpp +++ b/src/mbgl/util/http_header.cpp @@ -1,5 +1,7 @@ #include <mbgl/util/http_header.hpp> +#include <mbgl/util/string.hpp> + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" #pragma GCC diagnostic ignored "-Wunused-parameter" diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp index 6cfdc697e3..16f1d669f3 100644 --- a/src/mbgl/util/i18n.cpp +++ b/src/mbgl/util/i18n.cpp @@ -15,7 +15,7 @@ namespace { return codepoint >= first && codepoint <= last; \ } -// The following table comes from <http://www.unicode.org/Public/9.0.0/ucd/Blocks.txt>. +// The following table comes from <http://www.unicode.org/Public/10.0.0/ucd/Blocks.txt>. // Keep it synchronized with <http://www.unicode.org/Public/UCD/latest/ucd/Blocks.txt>. // DEFINE_IS_IN_UNICODE_BLOCK(BasicLatin, 0x0000, 0x007F) @@ -37,6 +37,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(ArabicSupplement, 0x0750, 0x077F) // DEFINE_IS_IN_UNICODE_BLOCK(NKo, 0x07C0, 0x07FF) // DEFINE_IS_IN_UNICODE_BLOCK(Samaritan, 0x0800, 0x083F) // DEFINE_IS_IN_UNICODE_BLOCK(Mandaic, 0x0840, 0x085F) +// DEFINE_IS_IN_UNICODE_BLOCK(Syriac Supplement, 0x0860, 0x086F) DEFINE_IS_IN_UNICODE_BLOCK(ArabicExtendedA, 0x08A0, 0x08FF) // DEFINE_IS_IN_UNICODE_BLOCK(Devanagari, 0x0900, 0x097F) // DEFINE_IS_IN_UNICODE_BLOCK(Bengali, 0x0980, 0x09FF) @@ -239,9 +240,12 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF) // DEFINE_IS_IN_UNICODE_BLOCK(Takri, 0x11680, 0x116CF) // DEFINE_IS_IN_UNICODE_BLOCK(Ahom, 0x11700, 0x1173F) // DEFINE_IS_IN_UNICODE_BLOCK(WarangCiti, 0x118A0, 0x118FF) +// DEFINE_IS_IN_UNICODE_BLOCK(ZanabazarSquare, 0x11A00, 0x11A4F) +// DEFINE_IS_IN_UNICODE_BLOCK(Soyombo, 0x11A50, 0x11AAF) // DEFINE_IS_IN_UNICODE_BLOCK(PauCinHau, 0x11AC0, 0x11AFF) // DEFINE_IS_IN_UNICODE_BLOCK(Bhaiksuki, 0x11C00, 0x11C6F) // DEFINE_IS_IN_UNICODE_BLOCK(Marchen, 0x11C70, 0x11CBF) +// DEFINE_IS_IN_UNICODE_BLOCK(MasaramGondi, 0x11D00, 0x11D5F) // DEFINE_IS_IN_UNICODE_BLOCK(Cuneiform, 0x12000, 0x123FF) // DEFINE_IS_IN_UNICODE_BLOCK(CuneiformNumbersandPunctuation, 0x12400, 0x1247F) // DEFINE_IS_IN_UNICODE_BLOCK(EarlyDynasticCuneiform, 0x12480, 0x1254F) @@ -256,6 +260,8 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF) // DEFINE_IS_IN_UNICODE_BLOCK(Tangut, 0x17000, 0x187FF) // DEFINE_IS_IN_UNICODE_BLOCK(TangutComponents, 0x18800, 0x18AFF) // DEFINE_IS_IN_UNICODE_BLOCK(KanaSupplement, 0x1B000, 0x1B0FF) +// DEFINE_IS_IN_UNICODE_BLOCK(KanaExtendedA, 0x1B100, 0x1B12F) +// DEFINE_IS_IN_UNICODE_BLOCK(Nushu, 0x1B170, 0x1B2FF) // DEFINE_IS_IN_UNICODE_BLOCK(Duployan, 0x1BC00, 0x1BC9F) // DEFINE_IS_IN_UNICODE_BLOCK(ShorthandFormatControls, 0x1BCA0, 0x1BCAF) // DEFINE_IS_IN_UNICODE_BLOCK(ByzantineMusicalSymbols, 0x1D000, 0x1D0FF) @@ -286,6 +292,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF) // DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionC, 0x2A700, 0x2B73F) // DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionD, 0x2B740, 0x2B81F) // DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionE, 0x2B820, 0x2CEAF) +// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionF, 0x2CEB0, 0x2EBEF) // DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibilityIdeographsSupplement, 0x2F800, 0x2FA1F) // DEFINE_IS_IN_UNICODE_BLOCK(Tags, 0xE0000, 0xE007F) // DEFINE_IS_IN_UNICODE_BLOCK(VariationSelectorsSupplement, 0xE0100, 0xE01EF) @@ -311,7 +318,7 @@ const std::map<char16_t, char16_t> verticalPunctuation = { { u'{', u'︷' }, { u'|', u'―' }, { u'}', u'︸' }, { u'⦅', u'︵' }, { u'⦆', u'︶' }, { u'。', u'︒' }, { u'「', u'﹁' }, { u'」', u'﹂' }, }; -} +} // namespace namespace mbgl { namespace util { @@ -375,11 +382,13 @@ bool allowsIdeographicBreaking(char16_t chr) { // return (isInTangut(chr) // || isInTangutComponents(chr) // || isInIdeographicSymbolsandPunctuation(chr) + // || isInNushu(chr) // || isInEnclosedIdeographicSupplement(chr) // || isInCJKUnifiedIdeographsExtensionB(chr) // || isInCJKUnifiedIdeographsExtensionC(chr) // || isInCJKUnifiedIdeographsExtensionD(chr) // || isInCJKUnifiedIdeographsExtensionE(chr) + // || isInCJKUnifiedIdeographsExtensionF(chr) // || isInCJKCompatibilityIdeographsSupplement(chr)); } @@ -393,7 +402,7 @@ bool allowsVerticalWritingMode(const std::u16string& string) { } // The following logic comes from -// <http://www.unicode.org/Public/vertical/revision-16/VerticalOrientation-16.txt>. +// <http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt>. // The data file denotes with “U” or “Tu” any codepoint that may be drawn // upright in vertical text but does not distinguish between upright and // “neutral” characters. @@ -457,6 +466,8 @@ bool hasUprightVerticalOrientation(char16_t chr) { // if (isInTangut(chr)) return true; // if (isInTangutComponents(chr)) return true; // if (isInKanaSupplement(chr)) return true; + // if (isInKanaExtendedA(chr)) return true; + // if (isInNushu(chr)) return true; // if (isInByzantineMusicalSymbols(chr)) return true; // if (isInMusicalSymbols(chr)) return true; // if (isInTaiXuanJingSymbols(chr)) return true; @@ -478,6 +489,7 @@ bool hasUprightVerticalOrientation(char16_t chr) { // if (isInCJKUnifiedIdeographsExtensionC(chr)) return true; // if (isInCJKUnifiedIdeographsExtensionD(chr)) return true; // if (isInCJKUnifiedIdeographsExtensionE(chr)) return true; + // if (isInCJKUnifiedIdeographsExtensionF(chr)) return true; // if (isInCJKCompatibilityIdeographsSupplement(chr)) return true; return false; diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp index c580357298..e6ce245c0e 100644 --- a/src/mbgl/util/intersection_tests.cpp +++ b/src/mbgl/util/intersection_tests.cpp @@ -19,7 +19,7 @@ bool polygonContainsPoint(const GeometryCoordinates& ring, const GeometryCoordin // Code from http://stackoverflow.com/a/1501725/331379. float distToSegmentSquared(const GeometryCoordinate& p, const GeometryCoordinate& v, const GeometryCoordinate& w) { if (v == w) return util::distSqr<float>(p, v); - const float l2 = util::distSqr<float>(v, w); + const auto l2 = util::distSqr<float>(v, w); const float t = float((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2; if (t < 0) return util::distSqr<float>(p, v); if (t > 1) return util::distSqr<float>(p, w); diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp index 9adc3b8988..6a6ed7b250 100644 --- a/src/mbgl/util/io.cpp +++ b/src/mbgl/util/io.cpp @@ -6,8 +6,6 @@ #include <sstream> #include <fstream> -#include <unistd.h> - namespace mbgl { namespace util { @@ -43,8 +41,8 @@ optional<std::string> readFile(const std::string &filename) { } void deleteFile(const std::string& filename) { - const int ret = unlink(filename.c_str()); - if (ret == -1) { + const int ret = std::remove(filename.c_str()); + if (ret != 0) { throw IOException(errno, "failed to unlink file"); } } diff --git a/src/mbgl/util/logging.cpp b/src/mbgl/util/logging.cpp index 939f1def64..0552eb36cb 100644 --- a/src/mbgl/util/logging.cpp +++ b/src/mbgl/util/logging.cpp @@ -1,6 +1,6 @@ #include <mbgl/util/logging.hpp> #include <mbgl/util/enum.hpp> -#include <mbgl/util/thread.hpp> +#include <mbgl/util/platform.hpp> #include <cstdio> #include <cstdarg> diff --git a/src/mbgl/util/longest_common_subsequence.hpp b/src/mbgl/util/longest_common_subsequence.hpp new file mode 100644 index 0000000000..522b5a86b1 --- /dev/null +++ b/src/mbgl/util/longest_common_subsequence.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include <cstddef> +#include <functional> +#include <iterator> +#include <vector> + +namespace mbgl { + +/* + Computes the longest common subsequence (LCS) of sequences A and B, represented + by pairs of random access iterators. The result is output to the provided output + iterator. Equality of elements is determined by the provided comparator, defaulting + to ==. + + The algorithm used is the O(ND) time and space algorithm from: + + Myers, Eugene W. An O(ND) Difference Algorithm and Its Variations. Algorithmica + (1986) 1: 251. http://xmailserver.org/diff2.pdf + + For understanding this algorithm, http://simplygenius.net/Article/DiffTutorial1 is + also helpful. + + TODO: implement the O(N) space refinement presented in the paper. +*/ +template <class InIt, class OutIt, class Equal> +OutIt longest_common_subsequence(InIt a, InIt endA, + InIt b, InIt endB, + OutIt outIt, + Equal eq) { + const std::ptrdiff_t N = endA - a; + const std::ptrdiff_t M = endB - b; + const std::ptrdiff_t D = N + M; + + if (D == 0) { + return outIt; + } + + std::vector<std::vector<std::ptrdiff_t>> vs; + + // Self-executing lambda to allow `return` to break from inner loop, and avoid shadowing `v`. + [&] () { + std::vector<std::ptrdiff_t> v; + v.resize(2 * D + 1); + v[1] = 0; + + // Core of the algorithm: greedily find farthest-reaching D-paths for increasing + // values of D. Store the farthest-reaching endpoints found in each iteration for + // later reconstructing the LCS. + for (std::ptrdiff_t d = 0; d <= D; ++d) { + for (std::ptrdiff_t k = -d; k <= d; k += 2) { + std::ptrdiff_t x = (k == -d || (k != d && v.at(k - 1 + D) < v.at(k + 1 + D))) + ? v.at(k + 1 + D) // moving down + : v.at(k - 1 + D) + 1; // moving right + + std::ptrdiff_t y = x - k; + + while (x < N && y < M && eq(a[x], b[y])) { + x++; + y++; + } + + v[k + D] = x; + + if (x >= N && y >= M) { + vs.push_back(v); + return; + } + } + + vs.push_back(v); + } + }(); + + std::ptrdiff_t x = N; + std::ptrdiff_t y = M; + + using E = typename std::iterator_traits<InIt>::value_type; + std::vector<E> lcsReverse; + + // Reconstruct the LCS using the farthest-reaching endpoints stored above. + for (std::ptrdiff_t d = vs.size() - 1; x > 0 || y > 0; --d) { + const std::vector<std::ptrdiff_t>& v = vs.at(d); + const std::ptrdiff_t k = x - y; + const bool down = (k == -d || (k != d && v.at(k - 1 + D) < v.at(k + 1 + D))); + const std::ptrdiff_t kPrev = down ? k + 1 : k - 1; + + x = v.at(kPrev + D); + y = x - kPrev; + + for (std::ptrdiff_t c = v[k + D]; c != (down ? x : x + 1); --c) { + lcsReverse.push_back(a[c - 1]); + } + } + + return std::copy(lcsReverse.rbegin(), lcsReverse.rend(), outIt); +} + +template < typename InIt, typename OutIt > +OutIt longest_common_subsequence(InIt a, InIt endA, + InIt b, InIt endB, + OutIt outIt) { + return longest_common_subsequence(a, endA, b, endB, outIt, std::equal_to<>()); +} + +} // namespace mbgl diff --git a/src/mbgl/util/mat2.hpp b/src/mbgl/util/mat2.hpp index 6a25ef0f1e..c463202daa 100644 --- a/src/mbgl/util/mat2.hpp +++ b/src/mbgl/util/mat2.hpp @@ -26,7 +26,7 @@ namespace mbgl { -typedef std::array<double, 4> mat2; +using mat2 = std::array<double, 4>; namespace matrix { diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp index d3d3617b7b..0ad0d371e5 100644 --- a/src/mbgl/util/mat4.cpp +++ b/src/mbgl/util/mat4.cpp @@ -336,10 +336,11 @@ void multiply(mat4& out, const mat4& a, const mat4& b) { } void transformMat4(vec4& out, const vec4& a, const mat4& m) { - out[0] = m[0] * a[0] + m[4] * a[1] + m[8] * a[2] + m[12] * a[3]; - out[1] = m[1] * a[0] + m[5] * a[1] + m[9] * a[2] + m[13] * a[3]; - out[2] = m[2] * a[0] + m[6] * a[1] + m[10] * a[2] + m[14] * a[3]; - out[3] = m[3] * a[0] + m[7] * a[1] + m[11] * a[2] + m[15] * a[3]; + double x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; } } // namespace matrix diff --git a/src/mbgl/util/math.hpp b/src/mbgl/util/math.hpp index f969ecaedd..c18ce0c254 100644 --- a/src/mbgl/util/math.hpp +++ b/src/mbgl/util/math.hpp @@ -77,9 +77,9 @@ T mag(const S& a) { return std::sqrt(a.x * a.x + a.y * a.y); } -template <typename S> +template <typename T = double, typename S> S unit(const S& a) { - auto magnitude = mag(a); + auto magnitude = mag<T>(a); if (magnitude == 0) { return a; } @@ -106,5 +106,18 @@ T smoothstep(T edge0, T edge1, T x) { return t * t * (T(3) - T(2) * t); } +template <typename T> +inline T division(const T dividend, const T divisor, const T nan) { + if (divisor == 0) { + if (dividend == 0) { + return nan; + } else { + return ::copysign(std::numeric_limits<T>::infinity(), dividend); + } + } else { + return dividend / divisor; + } +} + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp index fe24774b7c..339e74b250 100644 --- a/src/mbgl/util/offscreen_texture.cpp +++ b/src/mbgl/util/offscreen_texture.cpp @@ -11,20 +11,22 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default; class OffscreenTexture::Impl { public: - Impl(gl::Context& context_, const Size size_, OffscreenTextureAttachment type_) - : context(context_), size(std::move(size_)), type(type_) { + Impl(gl::Context& context_, const Size size_) + : context(context_), size(std::move(size_)) { + assert(!size.isEmpty()); + } + Impl(gl::Context& context_, + const Size size_, + gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_) + : context(context_), size(std::move(size_)), depth(&depth_) { assert(!size.isEmpty()); } void bind() { if (!framebuffer) { texture = context.createTexture(size, gl::TextureFormat::RGBA); - - if (type == OffscreenTextureAttachment::Depth) { - gl::Renderbuffer<gl::RenderbufferType::DepthComponent> depth = - context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size); - framebuffer = context.createFramebuffer(*texture, depth); - + if (depth) { + framebuffer = context.createFramebuffer(*texture, *depth); } else { framebuffer = context.createFramebuffer(*texture); } @@ -32,7 +34,8 @@ public: context.bindFramebuffer = framebuffer->framebuffer; } - context.activeTexture = 0; + context.activeTextureUnit = 0; + context.scissorTest = false; context.viewport = { 0, 0, size }; } @@ -52,15 +55,21 @@ public: private: gl::Context& context; const Size size; - OffscreenTextureAttachment type; optional<gl::Framebuffer> framebuffer; optional<gl::Texture> texture; + gl::Renderbuffer<gl::RenderbufferType::DepthComponent>* depth = nullptr; }; OffscreenTexture::OffscreenTexture(gl::Context& context, + const Size size) + : impl(std::make_unique<Impl>(context, std::move(size))) { + assert(!size.isEmpty()); +} + +OffscreenTexture::OffscreenTexture(gl::Context& context, const Size size, - OffscreenTextureAttachment type) - : impl(std::make_unique<Impl>(context, std::move(size), type)) { + gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer) + : impl(std::make_unique<Impl>(context, std::move(size), renderbuffer)) { assert(!size.isEmpty()); } diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp index ae96286340..7f7e0f0338 100644 --- a/src/mbgl/util/offscreen_texture.hpp +++ b/src/mbgl/util/offscreen_texture.hpp @@ -1,6 +1,5 @@ #pragma once -#include <mbgl/map/view.hpp> #include <mbgl/util/image.hpp> namespace mbgl { @@ -10,21 +9,18 @@ class Context; class Texture; } // namespace gl -enum class OffscreenTextureAttachment { - None, - Depth, -}; - -class OffscreenTexture : public View { +class OffscreenTexture { public: OffscreenTexture(gl::Context&, - Size size = { 256, 256 }, - OffscreenTextureAttachment type = OffscreenTextureAttachment::None); + Size size = { 256, 256 }); + OffscreenTexture(gl::Context&, + Size size, + gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&); ~OffscreenTexture(); OffscreenTexture(OffscreenTexture&&); OffscreenTexture& operator=(OffscreenTexture&&); - void bind() override; + void bind(); PremultipliedImage readStillImage(); diff --git a/src/mbgl/util/premultiply.cpp b/src/mbgl/util/premultiply.cpp index 219273d7cc..d9fb2480de 100644 --- a/src/mbgl/util/premultiply.cpp +++ b/src/mbgl/util/premultiply.cpp @@ -9,6 +9,7 @@ PremultipliedImage premultiply(UnassociatedImage&& src) { PremultipliedImage dst; dst.size = src.size; + src.size = { 0, 0 }; dst.data = std::move(src.data); uint8_t* data = dst.data.get(); @@ -29,6 +30,7 @@ UnassociatedImage unpremultiply(PremultipliedImage&& src) { UnassociatedImage dst; dst.size = src.size; + src.size = { 0, 0 }; dst.data = std::move(src.data); uint8_t* data = dst.data.get(); diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp deleted file mode 100644 index 184c6a8a12..0000000000 --- a/src/mbgl/util/thread.hpp +++ /dev/null @@ -1,173 +0,0 @@ -#pragma once - -#include <cassert> -#include <future> -#include <thread> -#include <atomic> -#include <utility> -#include <functional> - -#include <mbgl/util/run_loop.hpp> -#include <mbgl/util/thread_context.hpp> -#include <mbgl/util/platform.hpp> -#include <mbgl/util/util.hpp> - -namespace mbgl { -namespace util { - -// Manages a thread with Object. - -// Upon creation of this object, it launches a thread, creates an object of type Object in that -// thread, and then calls .start(); on that object. When the Thread<> object is destructed, the -// Object's .stop() function is called, and the destructor waits for thread termination. The -// Thread<> constructor blocks until the thread and the Object are fully created, so after the -// object creation, it's safe to obtain the Object stored in this thread. - -template <class Object> -class Thread { -public: - template <class... Args> - Thread(const ThreadContext&, Args&&... args); - ~Thread(); - - // Invoke object->fn(args...) asynchronously. - template <typename Fn, class... Args> - void invoke(Fn fn, Args&&... args) { - loop->invoke(bind(fn), std::forward<Args>(args)...); - } - - // Invoke object->fn(args...) asynchronously. The final argument to fn must be a callback. - // The provided callback is wrapped such that it is invoked, in the current thread (which - // must have a RunLoop), once for each time the invocation of fn invokes the wrapper, each - // time forwarding the passed arguments, until such time as the AsyncRequest is cancelled. - template <typename Fn, class... Args> - std::unique_ptr<AsyncRequest> - invokeWithCallback(Fn fn, Args&&... args) { - return loop->invokeWithCallback(bind(fn), std::forward<Args>(args)...); - } - - // Invoke object->fn(args...) asynchronously, but wait for the result. - template <typename Fn, class... Args> - auto invokeSync(Fn fn, Args&&... args) { - assert(!paused); - - using R = std::result_of_t<Fn(Object, Args&&...)>; - std::packaged_task<R ()> task(std::bind(fn, object, args...)); - std::future<R> future = task.get_future(); - loop->invoke(std::move(task)); - return future.get(); - } - - void pause() { - MBGL_VERIFY_THREAD(tid); - - assert(!paused); - - paused = std::make_unique<std::promise<void>>(); - resumed = std::make_unique<std::promise<void>>(); - - auto pausing = paused->get_future(); - - loop->invoke([this] { - auto resuming = resumed->get_future(); - paused->set_value(); - resuming.get(); - }); - - pausing.get(); - } - - void resume() { - MBGL_VERIFY_THREAD(tid); - - assert(paused); - - resumed->set_value(); - - resumed.reset(); - paused.reset(); - } - -private: - MBGL_STORE_THREAD(tid); - - Thread(const Thread&) = delete; - Thread(Thread&&) = delete; - Thread& operator=(const Thread&) = delete; - Thread& operator=(Thread&&) = delete; - - template <typename Fn> - auto bind(Fn fn) { - return [fn, this] (auto &&... args) { - return (object->*fn)(std::forward<decltype(args)>(args)...); - }; - } - - template <typename P, std::size_t... I> - void run(P&& params, std::index_sequence<I...>); - - std::promise<void> running; - std::promise<void> joinable; - - std::unique_ptr<std::promise<void>> paused; - std::unique_ptr<std::promise<void>> resumed; - - std::thread thread; - - Object* object = nullptr; - RunLoop* loop = nullptr; -}; - -template <class Object> -template <class... Args> -Thread<Object>::Thread(const ThreadContext& context, Args&&... args) { - // Note: We're using std::tuple<> to store the arguments because GCC 4.9 has a bug - // when expanding parameters packs captured in lambdas. - std::tuple<Args...> params = std::forward_as_tuple(::std::forward<Args>(args)...); - - thread = std::thread([&] { - platform::setCurrentThreadName(context.name); - - if (context.priority == ThreadPriority::Low) { - platform::makeThreadLowPriority(); - } - - run(std::move(params), std::index_sequence_for<Args...>{}); - }); - - running.get_future().get(); -} - -template <class Object> -template <typename P, std::size_t... I> -void Thread<Object>::run(P&& params, std::index_sequence<I...>) { - RunLoop loop_(RunLoop::Type::New); - loop = &loop_; - - Object object_(std::get<I>(std::forward<P>(params))...); - object = &object_; - - running.set_value(); - loop_.run(); - - loop = nullptr; - object = nullptr; - - joinable.get_future().get(); -} - -template <class Object> -Thread<Object>::~Thread() { - MBGL_VERIFY_THREAD(tid); - - if (paused) { - resume(); - } - - loop->stop(); - joinable.set_value(); - thread.join(); -} - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/thread_context.cpp b/src/mbgl/util/thread_context.cpp deleted file mode 100644 index fe64c2a686..0000000000 --- a/src/mbgl/util/thread_context.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include <mbgl/util/thread_context.hpp> -#include <utility> - -namespace mbgl { -namespace util { - -ThreadContext::ThreadContext(std::string name_, ThreadPriority priority_) - : name(std::move(name_)), - priority(priority_) { -} - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/thread_context.hpp b/src/mbgl/util/thread_context.hpp deleted file mode 100644 index a51dede404..0000000000 --- a/src/mbgl/util/thread_context.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include <string> - -namespace mbgl { -namespace util { - -enum class ThreadPriority : bool { - Regular, - Low, -}; - -struct ThreadContext { -public: - ThreadContext(std::string name, ThreadPriority priority = ThreadPriority::Regular); - - std::string name; - ThreadPriority priority; -}; - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/thread_local.hpp b/src/mbgl/util/thread_local.hpp index 9fddbd5bbc..b0e26356b4 100644 --- a/src/mbgl/util/thread_local.hpp +++ b/src/mbgl/util/thread_local.hpp @@ -1,12 +1,8 @@ #pragma once -#include <mbgl/util/logging.hpp> #include <mbgl/util/noncopyable.hpp> -#include <cassert> -#include <stdexcept> - -#include <pthread.h> +#include <memory> namespace mbgl { namespace util { @@ -14,40 +10,20 @@ namespace util { template <class T> class ThreadLocal : public noncopyable { public: - ThreadLocal() { - int ret = pthread_key_create(&key, [](void *ptr) { - delete reinterpret_cast<T *>(ptr); - }); - - if (ret) { - throw std::runtime_error("Failed to init local storage key."); - } - } - - ~ThreadLocal() { - if (pthread_key_delete(key)) { - Log::Error(Event::General, "Failed to delete local storage key."); - assert(false); - } + ThreadLocal(T* val) { + ThreadLocal(); + set(val); } - T* get() { - T* ret = reinterpret_cast<T*>(pthread_getspecific(key)); - if (!ret) { - return nullptr; - } + ThreadLocal(); + ~ThreadLocal(); - return ret; - } - - void set(T* ptr) { - if (pthread_setspecific(key, ptr)) { - throw std::runtime_error("Failed to set local storage."); - } - } + T* get(); + void set(T* ptr); private: - pthread_key_t key; + class Impl; + std::unique_ptr<Impl> impl; }; } // namespace util diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index b53e91162c..a5a1b1d70c 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -169,5 +169,26 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) { z); } +// Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs +// Computes the projected tiles for the lower left and upper right points of the bounds +// and uses that to compute the tile cover count +uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){ + + auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_); + auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_); + + auto x1 = floor(sw.x/ tileSize_); + auto x2 = floor((ne.x - 1) / tileSize_); + auto y1 = floor(sw.y/ tileSize_); + auto y2 = floor((ne.y - 1) / tileSize_); + + auto minX = std::fmax(std::min(x1, x2), 0); + auto maxX = std::max(x1, x2); + auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2); + auto maxY = (std::pow(2, zoom) - 1) - std::fmax(std::min(y1, y2), 0); + + return (maxX - minX + 1) * (maxY - minY + 1); +} + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp index 2d32d8bf41..3c7a4ee44a 100644 --- a/src/mbgl/util/tile_cover.hpp +++ b/src/mbgl/util/tile_cover.hpp @@ -18,5 +18,8 @@ int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize); std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z); std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z); +// Compute only the count of tiles needed for tileCover +uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize); + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/work_queue.cpp b/src/mbgl/util/work_queue.cpp deleted file mode 100644 index d0033e3ca2..0000000000 --- a/src/mbgl/util/work_queue.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include <mbgl/util/work_queue.hpp> -#include <mbgl/util/run_loop.hpp> - -#include <cassert> - -namespace mbgl { -namespace util { - -WorkQueue::WorkQueue() : runLoop(RunLoop::Get()) { -} - -WorkQueue::~WorkQueue() { - assert(runLoop == RunLoop::Get()); - - // Cancel all pending AsyncRequests. - while (!queue.empty()) { - queue.pop(); - } -} - -void WorkQueue::push(std::function<void()>&& fn) { - std::lock_guard<std::mutex> lock(queueMutex); - - auto workRequest = runLoop->invokeCancellable(std::bind(&WorkQueue::pop, this, std::move(fn))); - queue.push(std::move(workRequest)); -} - -void WorkQueue::pop(const std::function<void()>& fn) { - assert(runLoop == RunLoop::Get()); - - fn(); - - std::lock_guard<std::mutex> lock(queueMutex); - queue.pop(); -} - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/work_queue.hpp b/src/mbgl/util/work_queue.hpp deleted file mode 100644 index 3f6328fb57..0000000000 --- a/src/mbgl/util/work_queue.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/async_request.hpp> - -#include <functional> -#include <memory> -#include <mutex> -#include <queue> - -namespace mbgl { -namespace util { - -class RunLoop; - -// The WorkQueue will manage a queue of closures -// and it will make sure they get executed on the -// thread that created the WorkQueue. All pending -// works are canceled when the queue gets destructed. -class WorkQueue : private util::noncopyable { -public: - WorkQueue(); - ~WorkQueue(); - - // Push a closure to the queue. It is advised to - // only push tasks calling functions on the object - // that owns the queue to avoid use after free errors. - void push(std::function<void()>&&); - -private: - void pop(const std::function<void()>&); - - std::queue<std::unique_ptr<AsyncRequest>> queue; - std::mutex queueMutex; - - RunLoop* runLoop; -}; - -} // namespace util -} // namespace mbgl |