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:
Hans Goudey 2021-04-22 08:05:02 -05:00
parent f6efacfec7
commit b9a7b40924
16 changed files with 96 additions and 83 deletions

View File

@ -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. */

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;

View File

@ -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. */

View File

@ -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. */

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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. */

View File

@ -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. */

View File

@ -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 &params,
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. */

View File

@ -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. */

View File

@ -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 &params,
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);

View File

@ -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) {

View File

@ -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);
}
}
}