Files
test/source/blender/blenkernel/intern/attribute_access_intern.hh
Hans Goudey 024d7d12e2 Mesh: Move BVH storage to shared cache system
Avoid rebuilding BVH trees when meshes are copied.
Similar to the other uses of the shared cache system,
this can arbitrarily improve performance when meshes
are copied but not deformed and BVH building is the
main bottleneck. In a simple test file I got a 6x speedup.

The amount of code is also reduced and the system is
much simpler overall-- built out of common threading
patterns like `SharedCache` with its double-checked lock.
RAII is used in a few places to simplify memory management
too.

The downside is storing more `SharedCache` items in the
mesh runtime struct. That has a slight cost when copying
a small mesh many times, but we have ideas to improve that
in the future anyway (#104327).

Pull Request: https://projects.blender.org/blender/blender/pulls/130865
2024-12-04 00:17:17 +01:00

427 lines
14 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <functional>
#include "BLI_generic_pointer.hh"
#include "BLI_map.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
#include "BKE_geometry_set.hh"
namespace blender::bke {
/**
* Utility to group together multiple functions that are used to access custom data on geometry
* components in a generic way.
*/
struct CustomDataAccessInfo {
using CustomDataGetter = CustomData *(*)(void *owner);
using ConstCustomDataGetter = const CustomData *(*)(const void *owner);
using GetElementNum = int (*)(const void *owner);
using GetTagModifiedFunction = std::function<void()> (*)(void *owner, StringRef name);
CustomDataGetter get_custom_data;
ConstCustomDataGetter get_const_custom_data;
GetElementNum get_element_num;
GetTagModifiedFunction get_tag_modified_function;
};
/**
* A #BuiltinAttributeProvider is responsible for exactly one attribute on a geometry component.
* The attribute is identified by its name and has a fixed domain and type. Builtin attributes do
* not follow the same loose rules as other attributes, because they are mapped to internal
* "legacy" data structures. For example, some builtin attributes cannot be deleted.
*/
class BuiltinAttributeProvider {
public:
enum DeletableEnum {
Deletable,
NonDeletable,
};
protected:
const std::string name_;
const AttrDomain domain_;
const eCustomDataType data_type_;
const DeletableEnum deletable_;
const AttributeValidator validator_;
const GPointer default_value_;
public:
BuiltinAttributeProvider(std::string name,
const AttrDomain domain,
const eCustomDataType data_type,
const DeletableEnum deletable,
AttributeValidator validator = {},
const GPointer default_value = {})
: name_(std::move(name)),
domain_(domain),
data_type_(data_type),
deletable_(deletable),
validator_(validator),
default_value_(default_value)
{
}
virtual GAttributeReader try_get_for_read(const void *owner) const = 0;
virtual GAttributeWriter try_get_for_write(void *owner) const = 0;
virtual bool try_delete(void *owner) const = 0;
virtual bool try_create(void *onwer, const AttributeInit &initializer) const = 0;
virtual bool exists(const void *owner) const = 0;
StringRefNull name() const
{
return name_;
}
AttrDomain domain() const
{
return domain_;
}
eCustomDataType data_type() const
{
return data_type_;
}
AttributeValidator validator() const
{
return validator_;
}
};
/**
* A #DynamicAttributesProvider manages a set of named attributes on a geometry component. Each
* attribute has a name, domain and type.
*/
class DynamicAttributesProvider {
public:
virtual GAttributeReader try_get_for_read(const void *owner, StringRef attribute_id) const = 0;
virtual GAttributeWriter try_get_for_write(void *owner, StringRef attribute_id) const = 0;
virtual bool try_delete(void *owner, StringRef attribute_id) const = 0;
virtual bool try_create(void *owner,
const StringRef attribute_id,
const AttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer) const
{
UNUSED_VARS(owner, attribute_id, domain, data_type, initializer);
/* Some providers should not create new attributes. */
return false;
};
/**
* Return false when the iteration was stopped.
*/
virtual bool foreach_attribute(const void *owner,
FunctionRef<void(const AttributeIter &)> fn) const = 0;
virtual void foreach_domain(const FunctionRef<void(AttrDomain)> callback) const = 0;
};
/**
* This is the attribute provider for most user generated attributes.
*/
class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
static constexpr uint64_t supported_types_mask = CD_MASK_PROP_ALL;
AttrDomain domain_;
CustomDataAccessInfo custom_data_access_;
public:
CustomDataAttributeProvider(const AttrDomain domain,
const CustomDataAccessInfo custom_data_access)
: domain_(domain), custom_data_access_(custom_data_access)
{
}
GAttributeReader try_get_for_read(const void *owner, StringRef attribute_id) const final;
GAttributeWriter try_get_for_write(void *owner, StringRef attribute_id) const final;
bool try_delete(void *owner, StringRef attribute_id) const final;
bool try_create(void *owner,
StringRef attribute_id,
AttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer) const final;
bool foreach_attribute(const void *owner,
FunctionRef<void(const AttributeIter &)> fn) const final;
void foreach_domain(const FunctionRef<void(AttrDomain)> callback) const final
{
callback(domain_);
}
private:
bool type_is_supported(eCustomDataType data_type) const
{
return ((1ULL << data_type) & supported_types_mask) != 0;
}
};
/**
* This provider is used to provide access to builtin attributes. It supports making internal types
* available as different types.
*
* It also supports named builtin attributes, and will look up attributes in #CustomData by name
* if the stored type is the same as the attribute type.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
using UpdateOnChange = void (*)(void *owner);
const CustomDataAccessInfo custom_data_access_;
const UpdateOnChange update_on_change_;
public:
BuiltinCustomDataLayerProvider(std::string attribute_name,
const AttrDomain domain,
const eCustomDataType data_type,
const DeletableEnum deletable,
const CustomDataAccessInfo custom_data_access,
const UpdateOnChange update_on_change,
const AttributeValidator validator = {},
const GPointer default_value = {})
: BuiltinAttributeProvider(
std::move(attribute_name), domain, data_type, deletable, validator, default_value),
custom_data_access_(custom_data_access),
update_on_change_(update_on_change)
{
}
GAttributeReader try_get_for_read(const void *owner) const final;
GAttributeWriter try_get_for_write(void *owner) const final;
bool try_delete(void *owner) const final;
bool try_create(void *owner, const AttributeInit &initializer) const final;
bool exists(const void *owner) const final;
private:
bool layer_exists(const CustomData &custom_data) const;
};
/**
* This is a container for multiple attribute providers that are used by one geometry type
* (e.g. there is a set of attribute providers for mesh components).
*/
class GeometryAttributeProviders {
private:
/**
* Builtin attribute providers are identified by their name. Attribute names that are in this
* map will only be accessed using builtin attribute providers. Therefore, these providers have
* higher priority when an attribute name is looked up. Usually, that means that builtin
* providers are checked before dynamic ones.
*/
Map<std::string, const BuiltinAttributeProvider *> builtin_attribute_providers_;
/**
* An ordered list of dynamic attribute providers. The order is important because that is order
* in which they are checked when an attribute is looked up.
*/
Vector<const DynamicAttributesProvider *> dynamic_attribute_providers_;
/**
* All the domains that are supported by at least one of the providers above.
*/
VectorSet<AttrDomain> supported_domains_;
public:
GeometryAttributeProviders(Span<const BuiltinAttributeProvider *> builtin_attribute_providers,
Span<const DynamicAttributesProvider *> dynamic_attribute_providers)
: dynamic_attribute_providers_(dynamic_attribute_providers)
{
for (const BuiltinAttributeProvider *provider : builtin_attribute_providers) {
/* Use #add_new to make sure that no two builtin attributes have the same name. */
builtin_attribute_providers_.add_new(provider->name(), provider);
supported_domains_.add(provider->domain());
}
for (const DynamicAttributesProvider *provider : dynamic_attribute_providers) {
provider->foreach_domain([&](AttrDomain domain) { supported_domains_.add(domain); });
}
}
const Map<std::string, const BuiltinAttributeProvider *> &builtin_attribute_providers() const
{
return builtin_attribute_providers_;
}
Span<const DynamicAttributesProvider *> dynamic_attribute_providers() const
{
return dynamic_attribute_providers_;
}
Span<AttrDomain> supported_domains() const
{
return supported_domains_;
}
};
namespace attribute_accessor_functions {
template<const GeometryAttributeProviders &providers>
inline bool is_builtin(const void * /*owner*/, const StringRef attribute_id)
{
if (bke::attribute_name_is_anonymous(attribute_id)) {
return false;
}
const StringRef name = attribute_id;
return providers.builtin_attribute_providers().contains_as(name);
}
template<const GeometryAttributeProviders &providers>
inline GAttributeReader lookup(const void *owner, const StringRef attribute_id)
{
if (!bke::attribute_name_is_anonymous(attribute_id)) {
const StringRef name = attribute_id;
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr))
{
return provider->try_get_for_read(owner);
}
}
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
GAttributeReader attribute = provider->try_get_for_read(owner, attribute_id);
if (attribute) {
return attribute;
}
}
return {};
}
template<const GeometryAttributeProviders &providers>
inline void foreach_attribute(const void *owner,
const FunctionRef<void(const AttributeIter &)> fn,
const AttributeAccessor &accessor)
{
Set<StringRef, 16> handled_attribute_ids;
for (const BuiltinAttributeProvider *provider : providers.builtin_attribute_providers().values())
{
if (provider->exists(owner)) {
const auto get_fn = [&]() { return provider->try_get_for_read(owner); };
AttributeIter iter{provider->name(), provider->domain(), provider->data_type(), get_fn};
iter.is_builtin = true;
iter.accessor = &accessor;
fn(iter);
if (iter.is_stopped()) {
return;
}
handled_attribute_ids.add(iter.name);
}
}
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
const bool continue_loop = provider->foreach_attribute(owner, [&](const AttributeIter &iter) {
if (handled_attribute_ids.add(iter.name)) {
iter.accessor = &accessor;
fn(iter);
}
});
if (!continue_loop) {
return;
}
}
}
template<const GeometryAttributeProviders &providers>
inline AttributeValidator lookup_validator(const void * /*owner*/,
const blender::StringRef attribute_id)
{
if (bke::attribute_name_is_anonymous(attribute_id)) {
return {};
}
const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(attribute_id, nullptr);
if (!provider) {
return {};
}
return provider->validator();
}
template<const GeometryAttributeProviders &providers>
inline GAttributeWriter lookup_for_write(void *owner, const StringRef attribute_id)
{
if (!bke::attribute_name_is_anonymous(attribute_id)) {
const StringRef name = attribute_id;
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr))
{
return provider->try_get_for_write(owner);
}
}
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
GAttributeWriter attribute = provider->try_get_for_write(owner, attribute_id);
if (attribute) {
return attribute;
}
}
return {};
}
template<const GeometryAttributeProviders &providers>
inline bool remove(void *owner, const StringRef attribute_id)
{
if (!bke::attribute_name_is_anonymous(attribute_id)) {
const StringRef name = attribute_id;
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr))
{
return provider->try_delete(owner);
}
}
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
if (provider->try_delete(owner, attribute_id)) {
return true;
}
}
return false;
}
template<const GeometryAttributeProviders &providers>
inline bool add(void *owner,
const StringRef attribute_id,
AttrDomain domain,
eCustomDataType data_type,
const AttributeInit &initializer)
{
if (!bke::attribute_name_is_anonymous(attribute_id)) {
const StringRef name = attribute_id;
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr))
{
if (provider->domain() != domain) {
return false;
}
if (provider->data_type() != data_type) {
return false;
}
return provider->try_create(owner, initializer);
}
}
for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) {
if (provider->try_create(owner, attribute_id, domain, data_type, initializer)) {
return true;
}
}
return false;
}
template<const GeometryAttributeProviders &providers>
inline AttributeAccessorFunctions accessor_functions_for_providers()
{
return AttributeAccessorFunctions{nullptr,
nullptr,
is_builtin<providers>,
lookup<providers>,
nullptr,
foreach_attribute<providers>,
lookup_validator<providers>,
lookup_for_write<providers>,
remove<providers>,
add<providers>};
}
} // namespace attribute_accessor_functions
} // namespace blender::bke