diff --git a/include/mbgl/gfx/drawable.hpp b/include/mbgl/gfx/drawable.hpp index 1d26fcee5..51cc93418 100644 --- a/include/mbgl/gfx/drawable.hpp +++ b/include/mbgl/gfx/drawable.hpp @@ -11,6 +11,7 @@ namespace mbgl { +class Color; class PaintParameters; namespace gfx { @@ -71,6 +72,9 @@ public: /// Get the tweakers attached to this drawable const std::vector& getTweakers() const { return tweakers; } + // Reset a single color attribute for all vertexes + virtual void resetColor(const Color&) = 0; + protected: util::SimpleIdentity uniqueID; gfx::ShaderProgramBasePtr shader; diff --git a/include/mbgl/gfx/drawable_builder.hpp b/include/mbgl/gfx/drawable_builder.hpp index 567c74220..4a759adf5 100644 --- a/include/mbgl/gfx/drawable_builder.hpp +++ b/include/mbgl/gfx/drawable_builder.hpp @@ -60,6 +60,11 @@ public: const Color& getColor() const; void setColor(const Color& value); + enum class ColorMode { PerDrawable, PerVertex }; + /// Set how the color value is used + /// This should not be changed while a build is in progress + void setColorMode(ColorMode mode) { colorMode = mode; } + DepthMaskType getDepthType() const { return depthType; } void setDepthType(DepthMaskType value) { depthType = value; } @@ -98,6 +103,7 @@ protected: DrawablePtr currentDrawable; std::vector drawables; std::vector tweakers; + ColorMode colorMode = ColorMode::PerVertex; struct Impl; std::unique_ptr impl; diff --git a/include/mbgl/gfx/vertex_attribute.hpp b/include/mbgl/gfx/vertex_attribute.hpp index 5daedc5e7..c8df1d680 100644 --- a/include/mbgl/gfx/vertex_attribute.hpp +++ b/include/mbgl/gfx/vertex_attribute.hpp @@ -70,16 +70,38 @@ public: const ElementType& get(std::size_t i) const { return items[i]; } + void reserve(std::size_t count) { items.reserve(count); } + template const ElementType& set(std::size_t i, T value) { - dirty = true; // need to rebuild the raw data next time - items.resize(std::max(items.size(), i + 1)); + if (items.size() < i + 1) { + items.resize(std::max(items.size(), i + 1)); + setDirty(); // need to rebuild the raw data next time + } + if (!isDirty()) { + if (std::holds_alternative(items[i])) { + // TODO: epsilon for floats? + if (std::get(items[i]) != value) { + setDirty(); + } + } else { + // different types + setDirty(); + } + } return items[i] = value; } - bool getDirty() const { return dirty; } - void clearDirty() { - dirty = false; + void clear() { + if (!items.empty()) { + setDirty(); + } + items.clear(); + } + + bool isDirty() const { return dirty; } + void setDirty() { + dirty = true; rawData.clear(); } @@ -159,8 +181,10 @@ public: } /// Indicates whether any values have changed - bool isDirty() const; - void resetDirty(); + bool isDirty() const { + return std::any_of(attrs.begin(), attrs.end(), + [](const auto& kv){ return kv.second && kv.second->isDirty(); }); + } using ResolveDelegate = std::function; /// Call the provided delegate with each value, providing the override if one exists. diff --git a/include/mbgl/gl/drawable_gl.hpp b/include/mbgl/gl/drawable_gl.hpp index ea18a7a00..2de566a74 100644 --- a/include/mbgl/gl/drawable_gl.hpp +++ b/include/mbgl/gl/drawable_gl.hpp @@ -2,11 +2,11 @@ #include #include +#include #include namespace mbgl { - namespace gfx { class IndexBuffer; @@ -43,7 +43,17 @@ public: const gfx::UniqueVertexBufferResource& getBuffer() const; const gfx::IndexBuffer& getIndexBuffer() const; - + + /// Reset a single color attribute for all vertexes + void resetColor(const Color&) override; + + static gfx::VertexAttribute::float4 colorAttrValue(const Color& color) { + const auto components = color.toArray(); + return { static_cast(components[0]/255.0), + static_cast(components[1]/255.0), + static_cast(components[2]/255.0), + static_cast(components[3]) }; + } protected: class Impl; const std::unique_ptr impl; diff --git a/include/mbgl/renderer/change_request.hpp b/include/mbgl/renderer/change_request.hpp index 3b2ddcc99..6539818ad 100644 --- a/include/mbgl/renderer/change_request.hpp +++ b/include/mbgl/renderer/change_request.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -34,6 +35,17 @@ using ChangeRequestPtr = std::shared_ptr; using UniqueChangeRequest = std::unique_ptr; using UniqueChangeRequestVec = std::vector; +/** + Base for drawable-related change requests + */ +class DrawableRefChangeRequest : public ChangeRequest { +protected: + DrawableRefChangeRequest(util::SimpleIdentity id) : drawableID(id) { } + DrawableRefChangeRequest(const DrawableRefChangeRequest&) = default; + + util::SimpleIdentity drawableID; +}; + /** Add a new drawable to the scene @@ -57,16 +69,30 @@ protected: /** Remove a drawable from the scene */ -class RemoveDrawableRequest : public ChangeRequest { +class RemoveDrawableRequest : public DrawableRefChangeRequest { public: - RemoveDrawableRequest(util::SimpleIdentity drawableId_) : drawableId(drawableId_) { } - RemoveDrawableRequest(const RemoveDrawableRequest& other) : drawableId(other.drawableId) { } + RemoveDrawableRequest(util::SimpleIdentity id) : DrawableRefChangeRequest(id) { } + RemoveDrawableRequest(const RemoveDrawableRequest&) = default; + + void execute(RenderOrchestrator &) override; +}; + + +/** + Change the color of all vertexes + */ +class ResetColorRequest : public DrawableRefChangeRequest { +public: + ResetColorRequest(util::SimpleIdentity id, Color color) : + DrawableRefChangeRequest(id), + newColor(color) + { } + ResetColorRequest(const ResetColorRequest& other) = default; void execute(RenderOrchestrator &) override; protected: - util::SimpleIdentity drawableId; + Color newColor; }; - } // namespace mbgl diff --git a/src/mbgl/gfx/drawable_builder.cpp b/src/mbgl/gfx/drawable_builder.cpp index 4424aa515..3e457fe79 100644 --- a/src/mbgl/gfx/drawable_builder.cpp +++ b/src/mbgl/gfx/drawable_builder.cpp @@ -69,14 +69,18 @@ void DrawableBuilder::addTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1 impl->vertices.emplace_back(Impl::VT({{{ x1, y1 }}})); impl->vertices.emplace_back(Impl::VT({{{ x2, y2 }}})); impl->indexes.emplace_back(n, n+1, n+2); - impl->colors.insert(impl->colors.end(), 3, impl->currentColor); + if (colorMode == ColorMode::PerVertex) { + impl->colors.insert(impl->colors.end(), 3, impl->currentColor); + } } void DrawableBuilder::appendTriangle(int16_t x0, int16_t y0) { const auto n = (uint16_t)impl->vertices.elements(); impl->vertices.emplace_back(Impl::VT({{{ x0, y0 }}})); impl->indexes.emplace_back(n-2, n-1, n); - impl->colors.emplace_back(impl->currentColor); + if (colorMode == ColorMode::PerVertex) { + impl->colors.emplace_back(impl->currentColor); + } } void DrawableBuilder::addQuad(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { diff --git a/src/mbgl/gl/drawable_gl.cpp b/src/mbgl/gl/drawable_gl.cpp index 20f8ae418..009c5bae9 100644 --- a/src/mbgl/gl/drawable_gl.cpp +++ b/src/mbgl/gl/drawable_gl.cpp @@ -68,5 +68,12 @@ void DrawableGL::setVertexArray(gl::VertexArray&& vertexArray_, impl->indexBuffer = std::move(indexBuffer_); } +void DrawableGL::resetColor(const Color& newColor) { + if (auto* colorAttr = impl->vertexAttributes.get("a_color")) { + colorAttr->clear(); + colorAttr->set(0, colorAttrValue(newColor)); + } +} + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/drawable_gl_builder.cpp b/src/mbgl/gl/drawable_gl_builder.cpp index 843e2abf3..c3c682fe0 100644 --- a/src/mbgl/gl/drawable_gl_builder.cpp +++ b/src/mbgl/gl/drawable_gl_builder.cpp @@ -22,15 +22,20 @@ void DrawableGLBuilder::init() { } } if (auto colorAttr = attrs.getOrAdd("a_color")) { - std::size_t index = 0; - for (const auto& color : impl->colors) { - const auto comp = color.toArray(); - colorAttr->set(index++, gfx::VertexAttribute::float4 { - static_cast(comp[0]/255.0), - static_cast(comp[1]/255.0), - static_cast(comp[2]/255.0), - static_cast(comp[3]), - }); + // We should have either a single color or one per vertex. Otherwise, + // the color mode was probably changed after vertexes were added. + if (impl->colors.size() > 1 && impl->colors.size() != impl->vertices.elements()) { + impl->colors.clear(); + } + + if (impl->colors.empty()) { + colorAttr->set(0, DrawableGL::colorAttrValue(getColor())); + } else { + std::size_t index = 0; + colorAttr->reserve(impl->colors.size()); + for (const auto& color : impl->colors) { + colorAttr->set(index++, DrawableGL::colorAttrValue(color)); + } } } diff --git a/src/mbgl/renderer/change_request.cpp b/src/mbgl/renderer/change_request.cpp index 7b80b7604..da7a4ad55 100644 --- a/src/mbgl/renderer/change_request.cpp +++ b/src/mbgl/renderer/change_request.cpp @@ -8,7 +8,13 @@ void AddDrawableRequest::execute(RenderOrchestrator &orchestrator) { } void RemoveDrawableRequest::execute(RenderOrchestrator &orchestrator) { - orchestrator.removeDrawable(drawableId); + orchestrator.removeDrawable(drawableID); +} + +void ResetColorRequest::execute(RenderOrchestrator &orchestrator) { + if (auto& drawable = orchestrator.getDrawable(drawableID)) { + drawable->resetColor(newColor); + } } } // namespace mbgl diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp index d8ed70fd1..fb81135aa 100644 --- a/src/mbgl/renderer/render_orchestrator.cpp +++ b/src/mbgl/renderer/render_orchestrator.cpp @@ -770,6 +770,12 @@ void RenderOrchestrator::removeDrawable(const util::SimpleIdentity& drawableId) drawables.erase(drawableId); } +const gfx::DrawablePtr noDrawable; +const gfx::DrawablePtr& RenderOrchestrator::getDrawable(const util::SimpleIdentity& id) { + const auto hit = drawables.find(id); + return (hit != drawables.end()) ? hit->second : noDrawable; +} + void RenderOrchestrator::updateLayers(gfx::ShaderRegistry& shaders, const TransformState& state, const PropertyEvaluationParameters& evalParameters) { diff --git a/src/mbgl/renderer/render_orchestrator.hpp b/src/mbgl/renderer/render_orchestrator.hpp index f9aa1a307..75bf81eba 100644 --- a/src/mbgl/renderer/render_orchestrator.hpp +++ b/src/mbgl/renderer/render_orchestrator.hpp @@ -99,6 +99,8 @@ public: void addDrawable(gfx::DrawablePtr); void removeDrawable(const util::SimpleIdentity& drawableId); + const gfx::DrawablePtr& getDrawable(const util::SimpleIdentity&); + void updateLayers(gfx::ShaderRegistry&, const TransformState&, const PropertyEvaluationParameters&); diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 7f06920b5..b6863a6e1 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -117,7 +117,8 @@ void Renderer::Impl::render(const RenderTree& renderTree) { auto& shader = drawable.getShader(); // Generate a vertex array object for the drawable state, if necessary - if (shader && !drawableGL.getVertexArray().isValid()) { + if (shader && (!drawableGL.getVertexArray().isValid() || + drawableGL.getVertexAttributes().isDirty())) { const auto usage = gfx::BufferUsageType::StaticDraw; // Build index buffer diff --git a/src/mbgl/style/layers/background_layer_impl.cpp b/src/mbgl/style/layers/background_layer_impl.cpp index 7b1d9da2e..3dd87e96f 100644 --- a/src/mbgl/style/layers/background_layer_impl.cpp +++ b/src/mbgl/style/layers/background_layer_impl.cpp @@ -18,10 +18,10 @@ namespace style { BackgroundLayer::Impl::Impl(const Impl& other) : Layer::Impl(other), - shader(other.shader), + tileDrawables(other.tileDrawables), + lastColor(other.lastColor), paint(other.paint) { - // mutex, drawables, stats not copied } bool BackgroundLayer::Impl::hasLayoutDifference(const Layer::Impl&) const { @@ -46,17 +46,18 @@ void BackgroundLayer::Impl::layerAdded(gfx::ShaderRegistry& shaders, } void BackgroundLayer::Impl::layerRemoved(UniqueChangeRequestVec& changes) const { + // TODO: This isn't happening on style change, so old tile drawables are being left active + // Remove everything decltype(tileDrawables) localDrawables; { std::unique_lock guard(mutex); localDrawables = std::move(tileDrawables); } - - for (auto& kv : localDrawables) { - auto& drawable = kv.second; - changes.emplace_back(std::make_unique(drawable->getId())); - } + + removeDrawables( + localDrawables.cbegin(), localDrawables.cend(), changes, + [](auto& ii) { return ii->second->getId(); }); } void BackgroundLayer::Impl::update(const TransformState& state, @@ -64,8 +65,10 @@ void BackgroundLayer::Impl::update(const TransformState& state, UniqueChangeRequestVec& changes) const { std::unique_lock guard(mutex); - const auto uneval = paint.untransitioned(); - const auto eval = uneval.evaluate(evalParameters); + if (!unevaluated) { + unevaluated = paint.untransitioned(); + } + const auto evaluated = unevaluated->evaluate(evalParameters); //const auto passes = eval.get() == 0.0f // ? RenderPass::None @@ -79,13 +82,25 @@ void BackgroundLayer::Impl::update(const TransformState& state, //getCrossfade(evaluatedProperties).t != 1; std::optional color; - if (eval.get().from.empty()) { - const auto opacity = eval.get(); + if (evaluated.get().from.empty()) { + const auto opacity = evaluated.get(); if (opacity > 0.0f) { - color = eval.get() * eval.get(); + color = evaluated.get() * evaluated.get(); } } + // If the result is transparent or missing, just remove any existing drawables and stop + if (!color) { + removeDrawables( + tileDrawables.cbegin(), tileDrawables.cend(), changes, + [](auto& ii) { return ii->second->getId(); }); + tileDrawables.clear(); + return; + } + + const bool colorChange = (color != lastColor); + lastColor = color; + const auto zoom = state.getIntegerZoom(); const auto tileCover = util::tileCover(state, zoom); @@ -99,21 +114,24 @@ void BackgroundLayer::Impl::update(const TransformState& state, // For each existing tile drawable... for (auto iter = tileDrawables.begin(); iter != tileDrawables.end(); ) { + const auto& drawable = iter->second; + // Has this tile dropped out of the cover set? if (newTileIDs.find(iter->first) == newTileIDs.end()) { // remove it - const auto& drawable = iter->second; changes.emplace_back(std::make_unique(drawable->getId())); //Log::Warning(Event::General, "Removing drawable for " + util::toString(iter->first) + " total " + std::to_string(stats.tileDrawablesRemoved+1)); iter = tileDrawables.erase(iter); ++stats.tileDrawablesRemoved; - } else { - ++iter; + continue; } - } + ++iter; - if (!color) { - return; + // If the color evaluated to a new value, emit a change request updating all vertexes of + // the existing drawable to the new color. + if (colorChange) { + changes.emplace_back(std::make_unique(drawable->getId(), *color)); + } } std::unique_ptr builder; @@ -134,6 +152,7 @@ void BackgroundLayer::Impl::update(const TransformState& state, builder->setShader(shader); builder->addTweaker(std::make_shared()); // generally shared across drawables builder->setColor(*color); + builder->setColorMode(gfx::DrawableBuilder::ColorMode::PerDrawable); builder->setDepthType(gfx::DepthMaskType::ReadWrite); } diff --git a/src/mbgl/style/layers/background_layer_impl.hpp b/src/mbgl/style/layers/background_layer_impl.hpp index 1408f7adb..d22dd1dc5 100644 --- a/src/mbgl/style/layers/background_layer_impl.hpp +++ b/src/mbgl/style/layers/background_layer_impl.hpp @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include #include @@ -43,15 +45,27 @@ public: UniqueChangeRequestVec&) const override; private: + // Add a deletion change request for each drawable in a collection + template + static void removeDrawables(T beg, const T end, UniqueChangeRequestVec& changes, + std::function f) { + for (; beg != end; ++beg) { + changes.emplace_back(std::make_unique(f(beg))); + } + } + mutable std::mutex mutex; mutable gfx::ShaderProgramBasePtr shader; mutable std::unordered_map tileDrawables; + mutable std::optional lastColor; mutable struct Stats { size_t tileDrawablesAdded = 0; size_t tileDrawablesRemoved = 0; } stats; + mutable std::optional unevaluated; + public: BackgroundPaintProperties::Transitionable paint;