Geometry Nodes: Get attribute domain and type without allocation
Because we use virtual classes (and for other reasons), we had to do a small allocation when simply retrieving the data type and domain of an existing attribute. This happened quite a lot actually-- to determine these values for result attributes. This patch adds a simple function to retrieve this meta data without building the virtual array. This should lower the overhead of every attribute node, though the difference probably won't be noticible unless a tree has very many nodes. Differential Revision: https://developer.blender.org/D11047
This commit is contained in:
parent
f6efacfec7
commit
b9a7b40924
|
@ -102,6 +102,10 @@ class GeometryComponent {
|
|||
/* Return true when any attribute with this name exists, including built in attributes. */
|
||||
bool attribute_exists(const blender::StringRef attribute_name) const;
|
||||
|
||||
/* Return the data type and domain of an attribute with the given name if it exists. */
|
||||
std::optional<AttributeMetaData> attribute_get_meta_data(
|
||||
const blender::StringRef attribute_name) const;
|
||||
|
||||
/* Returns true when the geometry component supports this attribute domain. */
|
||||
bool attribute_domain_supported(const AttributeDomain domain) const;
|
||||
/* Can only be used with supported domain types. */
|
||||
|
|
|
@ -729,6 +729,20 @@ bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name
|
|||
return false;
|
||||
}
|
||||
|
||||
std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
|
||||
const StringRef attribute_name) const
|
||||
{
|
||||
std::optional<AttributeMetaData> result{std::nullopt};
|
||||
this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
|
||||
if (attribute_name == name) {
|
||||
result = meta_data;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
|
||||
std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
|
||||
{
|
||||
|
|
|
@ -126,13 +126,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
|||
StringRef source_name,
|
||||
StringRef result_name)
|
||||
{
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
ReadAttributeLookup source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
if (source_attribute) {
|
||||
return source_attribute.domain;
|
||||
std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
|
||||
if (source_info) {
|
||||
return source_info->domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
|
|
|
@ -47,15 +47,15 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
|||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the input attribute's domain if it exists. */
|
||||
ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name);
|
||||
if (input_attribute) {
|
||||
return input_attribute.domain;
|
||||
std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name);
|
||||
if (source_info) {
|
||||
return source_info->domain;
|
||||
}
|
||||
|
||||
return ATTR_DOMAIN_POINT;
|
||||
|
|
|
@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
|||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
|
|
|
@ -234,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
|||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
|
|
|
@ -55,13 +55,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
|||
StringRef source_name,
|
||||
StringRef result_name)
|
||||
{
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
ReadAttributeLookup source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
if (source_attribute) {
|
||||
return source_attribute.domain;
|
||||
std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
|
||||
if (source_info) {
|
||||
return source_info->domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
|
@ -75,14 +75,14 @@ static bool conversion_can_be_skipped(const GeometryComponent &component,
|
|||
if (source_name != result_name) {
|
||||
return false;
|
||||
}
|
||||
ReadAttributeLookup read_attribute = component.attribute_try_get_for_read(source_name);
|
||||
if (!read_attribute) {
|
||||
std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name);
|
||||
if (!info) {
|
||||
return false;
|
||||
}
|
||||
if (read_attribute.domain != result_domain) {
|
||||
if (info->domain != result_domain) {
|
||||
return false;
|
||||
}
|
||||
if (read_attribute.varray->type() != *bke::custom_data_type_to_cpp_type(result_type)) {
|
||||
if (info->data_type != result_type) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -68,13 +68,12 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node
|
|||
|
||||
namespace blender::nodes {
|
||||
|
||||
static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef attribute_name)
|
||||
static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(attribute_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
|
|
|
@ -318,13 +318,13 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
|||
StringRef source_name,
|
||||
StringRef result_name)
|
||||
{
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
ReadAttributeLookup source_attribute = component.attribute_try_get_for_read(source_name);
|
||||
if (source_attribute) {
|
||||
return source_attribute.domain;
|
||||
std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
|
||||
if (source_info) {
|
||||
return source_info->domain;
|
||||
}
|
||||
return ATTR_DOMAIN_POINT;
|
||||
}
|
||||
|
|
|
@ -200,9 +200,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
|||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
|
|
|
@ -141,9 +141,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
|
|||
StringRef result_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
|
||||
|
|
|
@ -196,12 +196,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
|
|||
|
||||
static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
const GeoNodeExecParams ¶ms,
|
||||
StringRef attribute_name)
|
||||
const StringRef name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(attribute_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the input domain chosen in the interface. */
|
||||
|
|
|
@ -49,20 +49,19 @@ static void geo_node_attribute_sample_texture_layout(uiLayout *layout,
|
|||
namespace blender::nodes {
|
||||
|
||||
static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
StringRef result_attribute_name,
|
||||
StringRef map_attribute_name)
|
||||
const StringRef result_name,
|
||||
const StringRef map_name)
|
||||
{
|
||||
/* Use the domain of the result attribute if it already exists. */
|
||||
ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(
|
||||
result_attribute_name);
|
||||
if (result_attribute) {
|
||||
return result_attribute.domain;
|
||||
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
|
||||
if (result_info) {
|
||||
return result_info->domain;
|
||||
}
|
||||
|
||||
/* Otherwise use the name of the map attribute. */
|
||||
ReadAttributeLookup map_attribute = component.attribute_try_get_for_read(map_attribute_name);
|
||||
if (map_attribute) {
|
||||
return map_attribute.domain;
|
||||
std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name);
|
||||
if (map_info) {
|
||||
return map_info->domain;
|
||||
}
|
||||
|
||||
/* The node won't execute in this case, but we still have to return a value. */
|
||||
|
|
|
@ -71,23 +71,23 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa
|
|||
|
||||
static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||
const GeoNodeExecParams ¶ms,
|
||||
StringRef result_name_x,
|
||||
StringRef result_name_y,
|
||||
StringRef result_name_z)
|
||||
const StringRef name_x,
|
||||
const StringRef name_y,
|
||||
const StringRef name_z)
|
||||
{
|
||||
/* Use the highest priority domain from any existing attribute outputs. */
|
||||
Vector<AttributeDomain, 3> output_domains;
|
||||
ReadAttributeLookup attribute_x = component.attribute_try_get_for_read(result_name_x);
|
||||
ReadAttributeLookup attribute_y = component.attribute_try_get_for_read(result_name_y);
|
||||
ReadAttributeLookup attribute_z = component.attribute_try_get_for_read(result_name_z);
|
||||
if (attribute_x) {
|
||||
output_domains.append(attribute_x.domain);
|
||||
std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x);
|
||||
std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y);
|
||||
std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z);
|
||||
if (info_x) {
|
||||
output_domains.append(info_x->domain);
|
||||
}
|
||||
if (attribute_y) {
|
||||
output_domains.append(attribute_y.domain);
|
||||
if (info_y) {
|
||||
output_domains.append(info_y->domain);
|
||||
}
|
||||
if (attribute_z) {
|
||||
output_domains.append(attribute_z.domain);
|
||||
if (info_z) {
|
||||
output_domains.append(info_z->domain);
|
||||
}
|
||||
if (output_domains.size() > 0) {
|
||||
return bke::attribute_domain_highest_priority(output_domains);
|
||||
|
|
|
@ -306,17 +306,14 @@ BLI_NOINLINE static void interpolate_existing_attributes(
|
|||
const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
|
||||
const Mesh &mesh = *source_component.get_for_read();
|
||||
|
||||
/* Use a dummy read without specifying a domain or data type in order to
|
||||
* get the existing attribute's domain. Interpolation is done manually based
|
||||
* on the bary coords in #interpolate_attribute. */
|
||||
ReadAttributeLookup dummy_attribute = source_component.attribute_try_get_for_read(
|
||||
std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data(
|
||||
attribute_name);
|
||||
if (!dummy_attribute) {
|
||||
if (!attribute_info) {
|
||||
i_instance += set_group.transforms.size();
|
||||
continue;
|
||||
}
|
||||
|
||||
const AttributeDomain source_domain = dummy_attribute.domain;
|
||||
const AttributeDomain source_domain = attribute_info->domain;
|
||||
GVArrayPtr source_attribute = source_component.attribute_get_for_read(
|
||||
attribute_name, source_domain, output_data_type, nullptr);
|
||||
if (!source_attribute) {
|
||||
|
|
|
@ -126,11 +126,11 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
|
|||
|
||||
if (found_socket->type == SOCK_STRING) {
|
||||
const std::string name = this->get_input<std::string>(found_socket->identifier);
|
||||
ReadAttributeLookup attribute = component.attribute_try_get_for_read(name);
|
||||
if (!attribute) {
|
||||
return default_type;
|
||||
std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
|
||||
if (info) {
|
||||
return info->data_type;
|
||||
}
|
||||
return bke::cpp_type_to_custom_data_type(attribute.varray->type());
|
||||
return default_type;
|
||||
}
|
||||
if (found_socket->type == SOCK_FLOAT) {
|
||||
return CD_PROP_FLOAT;
|
||||
|
@ -169,9 +169,9 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
|
|||
|
||||
if (found_socket->type == SOCK_STRING) {
|
||||
const std::string name = this->get_input<std::string>(found_socket->identifier);
|
||||
ReadAttributeLookup attribute = component.attribute_try_get_for_read(name);
|
||||
if (attribute) {
|
||||
input_domains.append(attribute.domain);
|
||||
std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
|
||||
if (info) {
|
||||
input_domains.append(info->domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue