Skip to content

Commit

Permalink
Don't use std::function in the renderer hot paths (#1883)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
mwilsnd and pre-commit-ci[bot] authored Nov 27, 2023
1 parent bf472b0 commit 71d4cea
Show file tree
Hide file tree
Showing 27 changed files with 260 additions and 311 deletions.
3 changes: 2 additions & 1 deletion include/mbgl/gfx/uniform_block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ class UniformBlockArray {
UniformBlockArray& operator=(const UniformBlockArray&);

/// Do something with each block
void visit(const std::function<void(const StringIdentity, const UniformBlock&)>& f) {
template <typename Func /* void(const StringIdentity, const UniformBlock&) */>
void visit(Func f) {
std::for_each(uniformBlockMap.begin(), uniformBlockMap.end(), [&](const auto& kv) {
if (kv.second) {
f(kv.first, *kv.second);
Expand Down
30 changes: 17 additions & 13 deletions include/mbgl/gfx/vertex_attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,27 +340,31 @@ class VertexAttributeArray {
void clear();

/// Do something with each attribute
void visitAttributes(const std::function<void(const StringIdentity, VertexAttribute&)>& f) {
template <typename Func /* void(StringIdentity, VertexAttribute&) */>
void visitAttributes(Func f) {
std::for_each(attrs.begin(), attrs.end(), [&](const auto& kv) {
if (kv.second) {
f(kv.first, *kv.second);
}
});
}

/// Do something with each attribute
void visitAttributes(const std::function<void(const StringIdentity, const VertexAttribute&)>& f) const {
std::for_each(attrs.begin(), attrs.end(), [&](const auto& kv) {
if (kv.second) {
f(kv.first, *kv.second);
}
});
}

using ResolveDelegate =
std::function<void(const StringIdentity, VertexAttribute&, const std::unique_ptr<VertexAttribute>&)>;
/// Call the provided delegate with each value, providing the override if one exists.
void resolve(const VertexAttributeArray& overrides, ResolveDelegate) const;
template <typename Func /* void(const StringIdentity, VertexAttribute&, const std::unique_ptr<VertexAttribute>&) */>
void resolve(const VertexAttributeArray& overrides, Func delegate) const {
for (auto& kv : attrs) {
delegate(kv.first, *kv.second, overrides.get(kv.first));
}
// For OpenGL, the shader attributes are established with reflection, and we have extra
// entries when we share attributes between, e.g., fill and fill-outline drawables.
#if !defined(NDEBUG) && MLN_RENDERER_BACKEND_METAL
// Every override should match a defined attribute.
for (const auto& kv : overrides.attrs) {
const auto hit = attrs.find(kv.first);
assert(hit != attrs.end());
}
#endif
}

VertexAttributeArray& operator=(VertexAttributeArray&&);
VertexAttributeArray& operator=(const VertexAttributeArray&);
Expand Down
162 changes: 137 additions & 25 deletions include/mbgl/renderer/layer_group.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#pragma once

#include <mbgl/gfx/drawable.hpp>
#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/identity.hpp>
#include <mbgl/util/logging.hpp>

#include <functional>
#include <memory>
#include <string>
#include <set>
#include <vector>

namespace mbgl {
Expand Down Expand Up @@ -37,8 +40,16 @@ using LayerTweakerWeakPtr = std::weak_ptr<LayerTweaker>;
A layer-like group of drawables, not a group of layers.
*/
class LayerGroupBase : public util::SimpleIdentifiable {
public:
enum class Type : uint8_t {
LayerGroup = 0,
TileLayerGroup,

Invalid = 255
};

protected:
LayerGroupBase(int32_t layerIndex, std::string name = std::string());
LayerGroupBase(int32_t layerIndex, std::string name = std::string(), Type type = Type::Invalid);

public:
LayerGroupBase(const LayerGroupBase&) = delete;
Expand All @@ -49,6 +60,9 @@ class LayerGroupBase : public util::SimpleIdentifiable {
bool getEnabled() const { return enabled; }
void setEnabled(bool value) { enabled = value; }

/// Get the type of layer group
Type getType() const noexcept { return type; }

/// Get the name of the layer group
const std::string& getName() const { return name; }
/// Set the name of the layer group
Expand Down Expand Up @@ -81,22 +95,14 @@ class LayerGroupBase : public util::SimpleIdentifiable {
/// Called at the end of each frame
virtual void postRender(RenderOrchestrator&, PaintParameters&) {}

/// Call the provided function for each drawable in priority order
virtual std::size_t visitDrawables(const std::function<void(gfx::Drawable&)>&&) = 0;
virtual std::size_t visitDrawables(const std::function<void(const gfx::Drawable&)>&&) const = 0;

/// Call the provided function for each drawable in undefined order, allowing for removal.
/// @param f A function called with each drawable, returning true to discard it and false to keep it
/// @return The number of items removed
virtual std::size_t removeDrawablesIf(const std::function<bool(gfx::Drawable&)>&& f) = 0;

/// Attach a tweaker to be run on this layer group for each frame.
/// Tweaker lifetime is controlled by the render layer and drawables, the layer group retains a weak reference.
void addLayerTweaker(const LayerTweakerPtr& tweaker) { layerTweakers.emplace_back(tweaker); }

void runTweakers(const RenderTree&, PaintParameters&);

protected:
const Type type;
bool enabled = true;
int32_t layerIndex;
std::vector<LayerTweakerWeakPtr> layerTweakers;
Expand All @@ -117,27 +123,70 @@ class TileLayerGroup : public LayerGroupBase {
std::vector<gfx::UniqueDrawable> removeDrawables(mbgl::RenderPass, const OverscaledTileID&);
void addDrawable(mbgl::RenderPass, const OverscaledTileID&, gfx::UniqueDrawable&&);

std::size_t visitDrawables(const std::function<void(gfx::Drawable&)>&&) override;
std::size_t visitDrawables(const std::function<void(const gfx::Drawable&)>&&) const override;
std::size_t removeDrawablesIf(const std::function<bool(gfx::Drawable&)>&&) override;
template <typename Func /* void(gfx::Drawable&) */>
std::size_t visitDrawables(Func f) {
assert(drawablesByTile.size() == sortedDrawables.size());
for (auto* drawable : sortedDrawables) {
f(*drawable);
}
return sortedDrawables.size();
}

template <typename Func /* bool(gfx::Drawable&) */>
std::size_t removeDrawablesIf(Func f) {
const auto oldSize = drawablesByTile.size();
for (auto i = drawablesByTile.begin(); i != drawablesByTile.end();) {
auto& drawable = i->second;
if (!f(*drawable)) {
// Not removed, keep going
++i;
} else {
// Removed, take it out of the collections
sortedDrawables.erase(drawable.get());
i = drawablesByTile.erase(i);
}
assert(drawablesByTile.size() == sortedDrawables.size());
}
return (oldSize - drawablesByTile.size());
}

/// Call the provided function for each drawable for the given tile
std::size_t visitDrawables(mbgl::RenderPass, const OverscaledTileID&, const std::function<void(gfx::Drawable&)>&&);
std::size_t visitDrawables(mbgl::RenderPass,
const OverscaledTileID&,
const std::function<void(const gfx::Drawable&)>&&) const;
template <typename Func /* void(gfx::Drawable&) */>
std::size_t visitDrawables(mbgl::RenderPass pass, const OverscaledTileID& tileID, Func f) {
assert(drawablesByTile.size() == sortedDrawables.size());
const auto range = drawablesByTile.equal_range({pass, tileID});
std::for_each(range.first, range.second, [&f](const auto& pair) { f(*pair.second); });
return std::distance(range.first, range.second);
}

std::size_t clearDrawables() override;

void setStencilTiles(RenderTiles);

protected:
struct Impl;
std::unique_ptr<Impl> impl;

// When stencil clipping is enabled for the layer, this is the set
// of tile IDs that need to be rendered to the stencil buffer.
RenderTiles stencilTiles;

struct TileLayerGroupTileKey {
mbgl::RenderPass renderPass;
OverscaledTileID tileID;
bool operator==(const TileLayerGroupTileKey& other) const {
return renderPass == other.renderPass && tileID == other.tileID;
}
struct hash {
size_t operator()(const TileLayerGroupTileKey& k) const {
return (std::hash<mbgl::RenderPass>()(k.renderPass) ^ std::hash<OverscaledTileID>()(k.tileID) << 1);
}
};
};

private:
using TileMap = std::unordered_multimap<TileLayerGroupTileKey, gfx::UniqueDrawable, TileLayerGroupTileKey::hash>;
TileMap drawablesByTile;

using DrawableMap = std::set<gfx::Drawable*, gfx::DrawableLessByPriority>;
DrawableMap sortedDrawables;
};

/**
Expand All @@ -154,15 +203,78 @@ class LayerGroup : public LayerGroupBase {
std::vector<gfx::UniqueDrawable> removeDrawables(mbgl::RenderPass);
void addDrawable(gfx::UniqueDrawable&&);

std::size_t visitDrawables(const std::function<void(gfx::Drawable&)>&&) override;
std::size_t visitDrawables(const std::function<void(const gfx::Drawable&)>&&) const override;
std::size_t removeDrawablesIf(const std::function<bool(gfx::Drawable&)>&&) override;
template <typename Func /* void(gfx::Drawable&) */>
std::size_t visitDrawables(Func f) {
for (const auto& item : drawables) {
if (item) {
f(*item);
}
}
return drawables.size();
}

template <typename Func /* bool(gfx::Drawable&) */>
std::size_t removeDrawablesIf(Func f) {
decltype(drawables) newSet;
const auto oldSize = drawables.size();
while (!drawables.empty()) {
// set members are immutable, since changes could affect its position, so extract each item
gfx::UniqueDrawable drawable = std::move(drawables.extract(drawables.begin()).value());
if (!f(*drawable)) {
// Not removed, keep it, but in a new set so that if the key value
// has increased, we don't see it again during this iteration.
newSet.emplace_hint(newSet.end(), std::move(drawable));
}
}
std::swap(drawables, newSet);
return (oldSize - drawables.size());
}

std::size_t clearDrawables() override;

protected:
struct Impl;
std::unique_ptr<Impl> impl;
using DrawableCollection = std::set<gfx::UniqueDrawable, gfx::DrawableLessByPriority>;
DrawableCollection drawables;
};

template <typename Func /* void(gfx::Drawable&) */>
void visitLayerGroupDrawables(mbgl::LayerGroupBase& layerGroup, Func dg) {
switch (layerGroup.getType()) {
case LayerGroupBase::Type::LayerGroup: {
static_cast<LayerGroup&>(layerGroup).visitDrawables(dg);
break;
}
case LayerGroupBase::Type::TileLayerGroup: {
static_cast<TileLayerGroup&>(layerGroup).visitDrawables(dg);
break;
}
default: {
#ifndef NDEBUG
mbgl::Log::Error(mbgl::Event::Render,
"Unknown layer group type: " + std::to_string(static_cast<uint8_t>(layerGroup.getType())));
#endif
break;
}
}
}

template <typename Func /* bool(gfx::Drawable&) */>
std::size_t removeLayerGroupDrawablesIf(mbgl::LayerGroupBase& layerGroup, Func dg) {
switch (layerGroup.getType()) {
case LayerGroupBase::Type::LayerGroup: {
return static_cast<LayerGroup&>(layerGroup).removeDrawablesIf(dg);
}
case LayerGroupBase::Type::TileLayerGroup: {
return static_cast<TileLayerGroup&>(layerGroup).removeDrawablesIf(dg);
}
default: {
#ifndef NDEBUG
mbgl::Log::Error(mbgl::Event::Render,
"Unknown layer group type: " + std::to_string(static_cast<uint8_t>(layerGroup.getType())));
#endif
return 0;
}
}
}

} // namespace mbgl
10 changes: 8 additions & 2 deletions include/mbgl/renderer/render_target.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,14 @@ class RenderTarget {
const LayerGroupBasePtr& getLayerGroup(const int32_t layerIndex) const;

/// Execute the given function for each contained layer group
void visitLayerGroups(std::function<void(LayerGroupBase&)>);
void visitLayerGroups(std::function<void(const LayerGroupBase&)>) const;
template <typename Func /* void(LayerGroupBase&) */>
void visitLayerGroups(Func f) {
for (auto& pair : layerGroupsByLayerIndex) {
if (pair.second) {
f(*pair.second);
}
}
}

/// Upload the layer groups
void upload(gfx::UploadPass& uploadPass);
Expand Down
15 changes: 0 additions & 15 deletions src/mbgl/gfx/vertex_attribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,21 +151,6 @@ void VertexAttributeArray::clear() {
attrs.clear();
}

void VertexAttributeArray::resolve(const VertexAttributeArray& overrides, ResolveDelegate delegate) const {
for (auto& kv : attrs) {
delegate(kv.first, *kv.second, overrides.get(kv.first));
}
// For OpenGL, the shader attributes are established with reflection, and we have extra
// entries when we share attributes between, e.g., fill and fill-outline drawables.
#if !defined(NDEBUG) && MLN_RENDERER_BACKEND_METAL
// Every override should match a defined attribute.
for (const auto& kv : overrides.attrs) {
const auto hit = attrs.find(kv.first);
assert(hit != attrs.end());
}
#endif
}

const UniqueVertexAttribute& VertexAttributeArray::add(const StringIdentity id,
std::unique_ptr<VertexAttribute>&& attr) {
const auto result = attrs.insert(std::make_pair(id, std::unique_ptr<VertexAttribute>()));
Expand Down
6 changes: 3 additions & 3 deletions src/mbgl/gl/vertex_attribute_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ int VertexAttributeGL::getStride(GLenum glType) {
}

// Copy the transformed type into the buffer, returning true if it works.
template <typename T, typename R>
static bool get(const gfx::VertexAttribute::ElementType& element, uint8_t* buffer, std::function<R(T)> f) {
template <typename T, typename R, typename Func /* R(T) */>
static bool get(const gfx::VertexAttribute::ElementType& element, uint8_t* buffer, Func func) {
if (auto* p = std::get_if<T>(&element)) {
*reinterpret_cast<R*>(buffer) = f(*p);
*reinterpret_cast<R*>(buffer) = func(*p);
return true;
}
return false;
Expand Down
Loading

0 comments on commit 71d4cea

Please sign in to comment.