Cleanup: Make Accumulate Attribute node more generic #104440

Merged
Hans Goudey merged 24 commits from mod_moder/blender:accumulate_generic_new into main 2023-05-08 20:36:18 +02:00
1 changed files with 68 additions and 89 deletions
Showing only changes of commit 9621f361cb - Show all commits

View File

@ -2,10 +2,6 @@
#include "BKE_attribute_math.hh" #include "BKE_attribute_math.hh"
#include "BLI_array.hh"
#include "BLI_generic_virtual_array.hh"
#include "BLI_virtual_array.hh"
#include "NOD_socket_search_link.hh" #include "NOD_socket_search_link.hh"
#include "node_geometry_util.hh" #include "node_geometry_util.hh"
@ -196,19 +192,19 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
} }
} }
class AccumulateFieldInput final : public bke::GeometryFieldInput { template<typename T> class AccumulateFieldInput final : public bke::GeometryFieldInput {
private: private:
GField input_; Field<T> input_;
Field<int> group_index_; Field<int> group_index_;
eAttrDomain source_domain_; eAttrDomain source_domain_;
AccumulationMode accumulation_mode_; AccumulationMode accumulation_mode_;
public: public:
AccumulateFieldInput(const eAttrDomain source_domain, AccumulateFieldInput(const eAttrDomain source_domain,
GField input, Field<T> input,
Field<int> group_index, Field<int> group_index,
AccumulationMode accumulation_mode) AccumulationMode accumulation_mode)
: bke::GeometryFieldInput(input.cpp_type(), "Accumulation"), : bke::GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
input_(input), input_(input),
group_index_(group_index), group_index_(group_index),
source_domain_(source_domain), source_domain_(source_domain),
@ -220,7 +216,7 @@ class AccumulateFieldInput final : public bke::GeometryFieldInput {
const IndexMask /*mask*/) const final const IndexMask /*mask*/) const final
{ {
const AttributeAccessor attributes = *context.attributes(); const AttributeAccessor attributes = *context.attributes();
const int64_t domain_size = attributes.domain_size(source_domain_); const int domain_size = attributes.domain_size(source_domain_);
if (domain_size == 0) { if (domain_size == 0) {
return {}; return {};
} }
@ -231,55 +227,46 @@ class AccumulateFieldInput final : public bke::GeometryFieldInput {
evaluator.add(input_); evaluator.add(input_);
evaluator.add(group_index_); evaluator.add(group_index_);
evaluator.evaluate(); evaluator.evaluate();
const GVArray g_values = evaluator.get_evaluated(0); const VArray<T> values = evaluator.get_evaluated<T>(0);
const VArray<int> group_indices = evaluator.get_evaluated<int>(1); const VArray<int> group_indices = evaluator.get_evaluated<int>(1);
GVArray g_output; Array<T> accumulations_out(domain_size);
mod_moder marked this conversation as resolved Outdated

I think I'd leave changing GVArray to GVArraySpan to a separate commit, the performance implications of that aren't obvious.

I think I'd leave changing `GVArray` to `GVArraySpan` to a separate commit, the performance implications of that aren't obvious.
attribute_math::convert_to_static_type(g_values.type(), [&](auto dummy) { if (group_indices.is_single()) {
using T = decltype(dummy); T accumulation = T();
if constexpr (is_same_any_v<T, int, float, float3>) { if (accumulation_mode_ == AccumulationMode::Leading) {
Array<T> outputs(domain_size); for (const int i : values.index_range()) {
const VArray<T> values = g_values.typed<T>(); accumulation = values[i] + accumulation;
accumulations_out[i] = accumulation;
if (group_indices.is_single()) {
T accumulation = T();
if (accumulation_mode_ == AccumulationMode::Leading) {
for (const int i : values.index_range()) {
accumulation = values[i] + accumulation;
outputs[i] = accumulation;
}
}
else {
for (const int i : values.index_range()) {
outputs[i] = accumulation;
accumulation = values[i] + accumulation;
}
}
} }
else {
Map<int, T> accumulations;
if (accumulation_mode_ == AccumulationMode::Leading) {
for (const int i : values.index_range()) {
T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]);
accumulation_value += values[i];
outputs[i] = accumulation_value;
}
}
else {
for (const int i : values.index_range()) {
T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]);
outputs[i] = accumulation_value;
accumulation_value += values[i];
}
}
}
g_output = VArray<T>::ForContainer(std::move(outputs));
} }
}); else {
for (const int i : values.index_range()) {
accumulations_out[i] = accumulation;
accumulation = values[i] + accumulation;
}
}
}
else {
Map<int, T> accumulations;
if (accumulation_mode_ == AccumulationMode::Leading) {
for (const int i : values.index_range()) {
T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]);
accumulation_value += values[i];
accumulations_out[i] = accumulation_value;
}
}
else {
for (const int i : values.index_range()) {
T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]);
accumulations_out[i] = accumulation_value;
accumulation_value += values[i];
}
}
}
return attributes.adapt_domain(std::move(g_output), source_domain_, context.domain()); return attributes.adapt_domain<T>(
VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain());
} }
uint64_t hash() const override uint64_t hash() const override
@ -306,15 +293,15 @@ class AccumulateFieldInput final : public bke::GeometryFieldInput {
} }
}; };
class TotalFieldInput final : public bke::GeometryFieldInput { template<typename T> class TotalFieldInput final : public bke::GeometryFieldInput {
private: private:
GField input_; Field<T> input_;
Field<int> group_index_; Field<int> group_index_;
eAttrDomain source_domain_; eAttrDomain source_domain_;
public: public:
TotalFieldInput(const eAttrDomain source_domain, GField input, Field<int> group_index) TotalFieldInput(const eAttrDomain source_domain, Field<T> input, Field<int> group_index)
: bke::GeometryFieldInput(input.cpp_type(), "Total Value"), : bke::GeometryFieldInput(CPPType::get<T>(), "Total Value"),
input_(input), input_(input),
group_index_(group_index), group_index_(group_index),
source_domain_(source_domain) source_domain_(source_domain)
@ -325,7 +312,7 @@ class TotalFieldInput final : public bke::GeometryFieldInput {
IndexMask /*mask*/) const final IndexMask /*mask*/) const final
{ {
const AttributeAccessor attributes = *context.attributes(); const AttributeAccessor attributes = *context.attributes();
const int64_t domain_size = attributes.domain_size(source_domain_); const int domain_size = attributes.domain_size(source_domain_);
if (domain_size == 0) { if (domain_size == 0) {
return {}; return {};
} }
@ -336,38 +323,29 @@ class TotalFieldInput final : public bke::GeometryFieldInput {
evaluator.add(input_); evaluator.add(input_);
evaluator.add(group_index_); evaluator.add(group_index_);
evaluator.evaluate(); evaluator.evaluate();
const GVArray g_values = evaluator.get_evaluated(0); const VArray<T> values = evaluator.get_evaluated<T>(0);
const VArray<int> group_indices = evaluator.get_evaluated<int>(1); const VArray<int> group_indices = evaluator.get_evaluated<int>(1);
GVArray g_outputs; if (group_indices.is_single()) {
T accumulation = T();
attribute_math::convert_to_static_type(g_values.type(), [&](auto dummy) { for (const int i : values.index_range()) {
using T = decltype(dummy); accumulation = values[i] + accumulation;
if constexpr (is_same_any_v<T, int, float, float3>) {
const VArray<T> values = g_values.typed<T>();
if (group_indices.is_single()) {
T accumulation = {};
for (const int i : values.index_range()) {
accumulation = values[i] + accumulation;
}
g_outputs = VArray<T>::ForSingle(accumulation, domain_size);
}
else {
Map<int, T> accumulations;
for (const int i : values.index_range()) {
T &value = accumulations.lookup_or_add_default(group_indices[i]);
value = value + values[i];
}
Array<T> outputs(domain_size);
for (const int i : values.index_range()) {
outputs[i] = accumulations.lookup(group_indices[i]);
}
g_outputs = VArray<T>::ForContainer(std::move(outputs));
}
} }
}); return VArray<T>::ForSingle(accumulation, domain_size);
}
return attributes.adapt_domain(std::move(g_outputs), source_domain_, context.domain()); Array<T> accumulations_out(domain_size);
Map<int, T> accumulations;
for (const int i : values.index_range()) {
T &value = accumulations.lookup_or_add_default(group_indices[i]);
value = value + values[i];
}
for (const int i : values.index_range()) {
accumulations_out[i] = accumulations.lookup(group_indices[i]);
}
return attributes.adapt_domain<T>(
VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain());
} }
uint64_t hash() const override uint64_t hash() const override
@ -413,24 +391,25 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<int> group_index_field = params.extract_input<Field<int>>("Group Index"); Field<int> group_index_field = params.extract_input<Field<int>>("Group Index");
attribute_math::convert_to_static_type(data_type, [&](auto dummy) { attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy); using T = decltype(dummy);
if constexpr (is_same_any_v<T, int, float, float3>) { if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
std::is_same_v<T, float3>) {
const std::string suffix = " " + identifier_suffix<T>(); const std::string suffix = " " + identifier_suffix<T>();
Field<T> input_field = params.extract_input<Field<T>>("Value" + suffix); Field<T> input_field = params.extract_input<Field<T>>("Value" + suffix);
if (params.output_is_required("Leading" + suffix)) { if (params.output_is_required("Leading" + suffix)) {
params.set_output( params.set_output(
"Leading" + suffix, "Leading" + suffix,
Field<T>{std::make_shared<AccumulateFieldInput>( Field<T>{std::make_shared<AccumulateFieldInput<T>>(
source_domain, input_field, group_index_field, AccumulationMode::Leading)}); source_domain, input_field, group_index_field, AccumulationMode::Leading)});
} }
if (params.output_is_required("Trailing" + suffix)) { if (params.output_is_required("Trailing" + suffix)) {
params.set_output( params.set_output(
"Trailing" + suffix, "Trailing" + suffix,
Field<T>{std::make_shared<AccumulateFieldInput>( Field<T>{std::make_shared<AccumulateFieldInput<T>>(
source_domain, input_field, group_index_field, AccumulationMode::Trailing)}); source_domain, input_field, group_index_field, AccumulationMode::Trailing)});
} }
if (params.output_is_required("Total" + suffix)) { if (params.output_is_required("Total" + suffix)) {
params.set_output("Total" + suffix, params.set_output("Total" + suffix,
Field<T>{std::make_shared<TotalFieldInput>( Field<T>{std::make_shared<TotalFieldInput<T>>(
source_domain, input_field, group_index_field)}); source_domain, input_field, group_index_field)});
} }
} }