Geometry Nodes: use array instead of map in GeometrySet

`GeometrySet` contains at most one component of each type.
Previously, a map was used to make sure that each component
type only exists once. The overhead of a map (especially with
inline storage) is rather large though. Since all component types
are known at compile time and the number of types is low,
a simple `std::array` works as well.

Some benefits of using `std::array` here:
* Looking up the component of a specific type is a bit faster.
* The size of `GeometrySet` becomes much smaller from 192 to 40 bytes.
* Debugging a `GeometrySet` in many tools becomes simpler because
  one can  easily see which components exists and which don't
This commit is contained in:
Jacques Lucke 2021-12-05 15:10:11 +01:00
parent d19443074a
commit b32f9bf801
3 changed files with 56 additions and 56 deletions

View File

@ -39,6 +39,8 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_CURVE = 4, GEO_COMPONENT_TYPE_CURVE = 4,
} GeometryComponentType; } GeometryComponentType;
#define GEO_COMPONENT_TYPE_ENUM_SIZE 5
void BKE_geometry_set_free(struct GeometrySet *geometry_set); void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_object_has_geometry_set_instances(const struct Object *ob); bool BKE_object_has_geometry_set_instances(const struct Object *ob);

View File

@ -262,7 +262,8 @@ inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryCompon
struct GeometrySet { struct GeometrySet {
private: private:
using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>; using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>;
blender::Map<GeometryComponentType, GeometryComponentPtr> components_; /* Indexed by #GeometryComponentType. */
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;
public: public:
GeometrySet(); GeometrySet();

View File

@ -118,25 +118,20 @@ GeometrySet &GeometrySet::operator=(GeometrySet &&other) = default;
*/ */
GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType component_type) GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType component_type)
{ {
return components_.add_or_modify( GeometryComponentPtr &component_ptr = components_[component_type];
component_type, if (!component_ptr) {
[&](GeometryComponentPtr *value_ptr) -> GeometryComponent & { /* If the component did not exist before, create a new one. */
/* If the component did not exist before, create a new one. */ component_ptr = GeometryComponent::create(component_type);
new (value_ptr) GeometryComponentPtr(GeometryComponent::create(component_type)); return *component_ptr;
return **value_ptr; }
}, if (component_ptr->is_mutable()) {
[&](GeometryComponentPtr *value_ptr) -> GeometryComponent & { /* If the referenced component is already mutable, return it directly. */
GeometryComponentPtr &value = *value_ptr; return *component_ptr;
if (value->is_mutable()) { }
/* If the referenced component is already mutable, return it directly. */ /* If the referenced component is shared, make a copy. The copy is not shared and is
return *value; * therefore mutable. */
} component_ptr = component_ptr->copy();
/* If the referenced component is shared, make a copy. The copy is not shared and is return *component_ptr;
* therefore mutable. */
GeometryComponent *copied_component = value->copy();
value = GeometryComponentPtr{copied_component};
return *copied_component;
});
} }
/** /**
@ -155,21 +150,17 @@ GeometryComponent *GeometrySet::get_component_ptr(GeometryComponentType type)
const GeometryComponent *GeometrySet::get_component_for_read( const GeometryComponent *GeometrySet::get_component_for_read(
GeometryComponentType component_type) const GeometryComponentType component_type) const
{ {
const GeometryComponentPtr *component = components_.lookup_ptr(component_type); return components_[component_type].get();
if (component != nullptr) {
return component->get();
}
return nullptr;
} }
bool GeometrySet::has(const GeometryComponentType component_type) const bool GeometrySet::has(const GeometryComponentType component_type) const
{ {
return components_.contains(component_type); return components_[component_type].has_value();
} }
void GeometrySet::remove(const GeometryComponentType component_type) void GeometrySet::remove(const GeometryComponentType component_type)
{ {
components_.remove(component_type); components_[component_type].reset();
} }
/** /**
@ -177,20 +168,20 @@ void GeometrySet::remove(const GeometryComponentType component_type)
*/ */
void GeometrySet::keep_only(const blender::Span<GeometryComponentType> component_types) void GeometrySet::keep_only(const blender::Span<GeometryComponentType> component_types)
{ {
for (auto it = components_.keys().begin(); it != components_.keys().end(); ++it) { for (GeometryComponentPtr &component_ptr : components_) {
const GeometryComponentType type = *it; if (component_ptr) {
if (!component_types.contains(type)) { if (!component_types.contains(component_ptr->type())) {
components_.remove(it); component_ptr.reset();
}
} }
} }
} }
void GeometrySet::add(const GeometryComponent &component) void GeometrySet::add(const GeometryComponent &component)
{ {
BLI_assert(!components_.contains(component.type())); BLI_assert(!components_[component.type()]);
component.user_add(); component.user_add();
GeometryComponentPtr component_ptr{const_cast<GeometryComponent *>(&component)}; components_[component.type()] = const_cast<GeometryComponent *>(&component);
components_.add_new(component.type(), std::move(component_ptr));
} }
/** /**
@ -199,8 +190,10 @@ void GeometrySet::add(const GeometryComponent &component)
Vector<const GeometryComponent *> GeometrySet::get_components_for_read() const Vector<const GeometryComponent *> GeometrySet::get_components_for_read() const
{ {
Vector<const GeometryComponent *> components; Vector<const GeometryComponent *> components;
for (const GeometryComponentPtr &ptr : components_.values()) { for (const GeometryComponentPtr &component_ptr : components_) {
components.append(ptr.get()); if (component_ptr) {
components.append(component_ptr.get());
}
} }
return components; return components;
} }
@ -236,27 +229,34 @@ std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
/* Remove all geometry components from the geometry set. */ /* Remove all geometry components from the geometry set. */
void GeometrySet::clear() void GeometrySet::clear()
{ {
components_.clear(); for (GeometryComponentPtr &component_ptr : components_) {
component_ptr.reset();
}
} }
/* Make sure that the geometry can be cached. This does not ensure ownership of object/collection /* Make sure that the geometry can be cached. This does not ensure ownership of object/collection
* instances. */ * instances. */
void GeometrySet::ensure_owns_direct_data() void GeometrySet::ensure_owns_direct_data()
{ {
for (GeometryComponentType type : components_.keys()) { for (GeometryComponentPtr &component_ptr : components_) {
const GeometryComponent *component = this->get_component_for_read(type); if (!component_ptr) {
if (!component->owns_direct_data()) { continue;
GeometryComponent &component_for_write = this->get_component_for_write(type);
component_for_write.ensure_owns_direct_data();
} }
if (component_ptr->owns_direct_data()) {
continue;
}
GeometryComponent &component_for_write = this->get_component_for_write(component_ptr->type());
component_for_write.ensure_owns_direct_data();
} }
} }
bool GeometrySet::owns_direct_data() const bool GeometrySet::owns_direct_data() const
{ {
for (const GeometryComponentPtr &component : components_.values()) { for (const GeometryComponentPtr &component_ptr : components_) {
if (!component->owns_direct_data()) { if (component_ptr) {
return false; if (!component_ptr->owns_direct_data()) {
return false;
}
} }
} }
return true; return true;
@ -328,23 +328,20 @@ bool GeometrySet::has_curve() const
/* Returns true when the geometry set has any data that is not an instance. */ /* Returns true when the geometry set has any data that is not an instance. */
bool GeometrySet::has_realized_data() const bool GeometrySet::has_realized_data() const
{ {
if (components_.is_empty()) { for (const GeometryComponentPtr &component_ptr : components_) {
return false; if (component_ptr) {
if (component_ptr->type() != GEO_COMPONENT_TYPE_INSTANCES) {
return true;
}
}
} }
if (components_.size() > 1) { return false;
return true;
}
/* Check if the only component is an #InstancesComponent. */
return this->get_component_for_read<InstancesComponent>() == nullptr;
} }
/* Return true if the geometry set has any component that isn't empty. */ /* Return true if the geometry set has any component that isn't empty. */
bool GeometrySet::is_empty() const bool GeometrySet::is_empty() const
{ {
if (components_.is_empty()) { return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() || this->has_volume() ||
return true;
}
return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() ||
this->has_instances()); this->has_instances());
} }