Refactor: Geometry Nodes: simplify parameter access

Now that every socket type uses `SocketValueVariant` since #144355, the
parameter access code can be simplified a bit.

This also adds a new `GeoNodesMultiInput<T>` type that's used to access the list
of inputs value multi-input sockets.

Pull Request: https://projects.blender.org/blender/blender/pulls/144409
This commit is contained in:
Jacques Lucke
2025-08-12 07:45:03 +02:00
parent f96a73961a
commit d9410660ca
12 changed files with 124 additions and 200 deletions

View File

@@ -14,6 +14,7 @@
#include "NOD_geometry_nodes_bundle.hh"
#include "NOD_geometry_nodes_closure.hh"
#include "NOD_geometry_nodes_list.hh"
#include "NOD_geometry_nodes_values.hh"
#include "DNA_meshdata_types.h"
@@ -40,6 +41,8 @@ BLI_CPP_TYPE_MAKE(blender::nodes::ListPtr, CPPTypeFlags::None);
BLI_CPP_TYPE_MAKE(blender::bke::GeometryNodesReferenceSet, CPPTypeFlags::None);
BLI_CPP_TYPE_MAKE(blender::bke::SocketValueVariant, CPPTypeFlags::Printable);
BLI_VECTOR_CPP_TYPE_MAKE(blender::bke::SocketValueVariant);
BLI_CPP_TYPE_MAKE(blender::nodes::GeoNodesMultiInput<blender::bke::SocketValueVariant>,
CPPTypeFlags::None);
void BKE_cpp_types_init()
{
@@ -64,4 +67,5 @@ void BKE_cpp_types_init()
BLI_CPP_TYPE_REGISTER(blender::bke::GeometryNodesReferenceSet);
BLI_CPP_TYPE_REGISTER(blender::bke::SocketValueVariant);
BLI_VECTOR_CPP_TYPE_REGISTER(blender::bke::SocketValueVariant);
BLI_CPP_TYPE_REGISTER(blender::nodes::GeoNodesMultiInput<blender::bke::SocketValueVariant>);
}

View File

@@ -1151,4 +1151,8 @@ class Vector {
template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
using RawVector = Vector<T, InlineBufferCapacity, RawAllocator>;
template<typename T> static constexpr bool is_Vector_v = false;
template<typename T, int64_t InlineBufferCapacity, typename Allocator>
static constexpr bool is_Vector_v<Vector<T, InlineBufferCapacity, Allocator>> = true;
} /* namespace blender */

View File

@@ -105,27 +105,42 @@ class GeoNodeExecParams {
*/
template<typename T> T extract_input(StringRef identifier)
{
if constexpr (std::is_enum_v<T>) {
return T(this->extract_input<int>(identifier));
}
else if constexpr (geo_nodes_type_stored_as_SocketValueVariant_v<T>) {
SocketValueVariant value_variant = this->extract_input<SocketValueVariant>(identifier);
return value_variant.extract<T>();
#ifndef NDEBUG
this->check_input_access(identifier);
#endif
const int index = this->get_input_index(identifier);
if constexpr (is_GeoNodesMultiInput_v<T>) {
using ValueT = typename T::value_type;
BLI_assert(node_.input_by_identifier(identifier)->is_multi_input());
if constexpr (std::is_same_v<ValueT, SocketValueVariant>) {
return params_.extract_input<T>(index);
}
else {
auto values_variants = params_.extract_input<GeoNodesMultiInput<SocketValueVariant>>(
index);
GeoNodesMultiInput<ValueT> values;
values.values.reserve(values_variants.values.size());
for (const int i : values_variants.values.index_range()) {
values.values.append(values_variants.values[i].extract<ValueT>());
}
return values;
}
}
else {
#ifndef NDEBUG
this->check_input_access(identifier, &CPPType::get<T>());
#endif
const int index = this->get_input_index(identifier);
T value = params_.extract_input<T>(index);
if constexpr (std::is_same_v<T, GeometrySet>) {
this->check_input_geometry_set(identifier, value);
}
SocketValueVariant value_variant = params_.extract_input<SocketValueVariant>(index);
if constexpr (std::is_same_v<T, SocketValueVariant>) {
BLI_assert(value.valid_for_socket(
eNodeSocketDatatype(node_.input_by_identifier(identifier)->type)));
return value_variant;
}
else if constexpr (std::is_enum_v<T>) {
return T(value_variant.extract<int>());
}
else {
T value = value_variant.extract<T>();
if constexpr (std::is_same_v<T, GeometrySet>) {
this->check_input_geometry_set(identifier, value);
}
return value;
}
return value;
}
}
@@ -137,27 +152,40 @@ class GeoNodeExecParams {
*/
template<typename T> T get_input(StringRef identifier) const
{
if constexpr (std::is_enum_v<T>) {
return T(this->get_input<int>(identifier));
}
else if constexpr (geo_nodes_type_stored_as_SocketValueVariant_v<T>) {
auto value_variant = this->get_input<SocketValueVariant>(identifier);
return value_variant.extract<T>();
#ifndef NDEBUG
this->check_input_access(identifier);
#endif
const int index = this->get_input_index(identifier);
if constexpr (is_GeoNodesMultiInput_v<T>) {
using ValueT = typename T::value_type;
BLI_assert(node_.input_by_identifier(identifier)->is_multi_input());
if constexpr (std::is_same_v<ValueT, SocketValueVariant>) {
return params_.get_input<T>(index);
}
else {
auto values_variants = params_.get_input<GeoNodesMultiInput<SocketValueVariant>>(index);
Vector<ValueT> values(values_variants.values.size());
for (const int i : values_variants.values.index_range()) {
values[i] = values_variants.values[i].extract<ValueT>();
}
return values;
}
}
else {
#ifndef NDEBUG
this->check_input_access(identifier, &CPPType::get<T>());
#endif
const int index = this->get_input_index(identifier);
const T &value = params_.get_input<T>(index);
if constexpr (std::is_same_v<T, GeometrySet>) {
this->check_input_geometry_set(identifier, value);
}
const SocketValueVariant &value_variant = params_.get_input<SocketValueVariant>(index);
if constexpr (std::is_same_v<T, SocketValueVariant>) {
BLI_assert(value.valid_for_socket(
eNodeSocketDatatype(node_.input_by_identifier(identifier)->type)));
return value_variant;
}
else if constexpr (std::is_enum_v<T>) {
return T(value_variant.get<int>());
}
else {
T value = value_variant.get<T>();
if constexpr (std::is_same_v<T, GeometrySet>) {
this->check_input_geometry_set(identifier, value);
}
return value;
}
return value;
}
}
@@ -177,23 +205,18 @@ class GeoNodeExecParams {
template<typename T> void set_output(StringRef identifier, T &&value)
{
using StoredT = std::decay_t<T>;
if constexpr (geo_nodes_type_stored_as_SocketValueVariant_v<StoredT>) {
this->set_output(identifier, SocketValueVariant::From(std::forward<T>(value)));
#ifndef NDEBUG
this->check_output_access(identifier);
#endif
if constexpr (std::is_same_v<StoredT, GeometrySet>) {
this->check_output_geometry_set(value);
}
const int index = this->get_output_index(identifier);
if constexpr (std::is_same_v<StoredT, SocketValueVariant>) {
params_.set_output(index, std::forward<T>(value));
}
else {
#ifndef NDEBUG
const CPPType &type = CPPType::get<StoredT>();
this->check_output_access(identifier, type);
if constexpr (std::is_same_v<StoredT, SocketValueVariant>) {
BLI_assert(value.valid_for_socket(
eNodeSocketDatatype(node_.output_by_identifier(identifier)->type)));
}
#endif
if constexpr (std::is_same_v<StoredT, GeometrySet>) {
this->check_output_geometry_set(value);
}
const int index = this->get_output_index(identifier);
params_.set_output(index, std::forward<T>(value));
params_.set_output(index, SocketValueVariant::From(std::forward<T>(value)));
}
}
@@ -315,8 +338,8 @@ class GeoNodeExecParams {
private:
/* Utilities for detecting common errors at when using this class. */
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
void check_output_access(StringRef identifier, const CPPType &value_type) const;
void check_input_access(StringRef identifier) const;
void check_output_access(StringRef identifier) const;
/* Find the active socket with the input name (not the identifier). */
const bNodeSocket *find_available_socket(const StringRef name) const;

View File

@@ -128,40 +128,19 @@ inline std::optional<T> BundleItemValue::as_socket_value(
}
converted_value = buffer;
}
if constexpr (geo_nodes_type_stored_as_SocketValueVariant_v<T>) {
const auto &value_variant = *static_cast<const bke::SocketValueVariant *>(converted_value);
return value_variant.get<T>();
}
return *static_cast<const T *>(converted_value);
}
template<typename T> constexpr bool is_valid_static_bundle_item_type()
{
if (geo_nodes_is_field_base_type_v<T>) {
return true;
}
if constexpr (fn::is_field_v<T>) {
return geo_nodes_is_field_base_type_v<typename T::base_type>;
}
if constexpr (is_same_any_v<T, BundlePtr, ClosurePtr, ListPtr>) {
return true;
}
return !geo_nodes_type_stored_as_SocketValueVariant_v<T>;
const auto &value_variant = *static_cast<const bke::SocketValueVariant *>(converted_value);
return value_variant.get<T>();
}
template<typename T> inline const bke::bNodeSocketType *socket_type_info_by_static_type()
{
if constexpr (fn::is_field_v<T>) {
if constexpr (geo_nodes_is_field_base_type_v<typename T::base_type>) {
const std::optional<eNodeSocketDatatype> socket_type =
bke::geo_nodes_base_cpp_type_to_socket_type(CPPType::get<typename T::base_type>());
BLI_assert(socket_type);
const bke::bNodeSocketType *socket_type_info = bke::node_socket_type_find_static(
*socket_type);
BLI_assert(socket_type_info);
return socket_type_info;
}
return nullptr;
const std::optional<eNodeSocketDatatype> socket_type =
bke::geo_nodes_base_cpp_type_to_socket_type(CPPType::get<typename T::base_type>());
BLI_assert(socket_type);
const bke::bNodeSocketType *socket_type_info = bke::node_socket_type_find_static(*socket_type);
BLI_assert(socket_type_info);
return socket_type_info;
}
else {
const std::optional<eNodeSocketDatatype> socket_type =
@@ -185,7 +164,6 @@ template<typename T> constexpr bool is_valid_internal_bundle_item_type()
template<typename T> inline std::optional<T> BundleItemValue::as() const
{
static_assert(is_valid_static_bundle_item_type<T>() || is_valid_internal_bundle_item_type<T>());
if constexpr (is_valid_internal_bundle_item_type<T>()) {
using SharingInfoT = typename T::element_type;
const auto *internal_value = std::get_if<BundleItemInternalValue>(&this->value);
@@ -246,9 +224,6 @@ template<typename T> inline std::optional<T> Bundle::lookup_path(const StringRef
template<typename T, typename Fn> inline void to_stored_type(T &&value, Fn &&fn)
{
using DecayT = std::decay_t<T>;
static_assert(
is_valid_static_bundle_item_type<DecayT>() || is_valid_internal_bundle_item_type<DecayT>() ||
is_same_any_v<DecayT, BundleItemValue, BundleItemSocketValue, BundleItemInternalValue>);
if constexpr (std::is_same_v<DecayT, BundleItemValue>) {
fn(std::forward<T>(value));
}
@@ -266,13 +241,8 @@ template<typename T, typename Fn> inline void to_stored_type(T &&value, Fn &&fn)
fn(BundleItemValue{BundleItemInternalValue{ImplicitSharingPtr{sharing_info}}});
}
else if (const bke::bNodeSocketType *socket_type = socket_type_info_by_static_type<DecayT>()) {
if constexpr (geo_nodes_type_stored_as_SocketValueVariant_v<DecayT>) {
auto value_variant = bke::SocketValueVariant::From(std::forward<T>(value));
fn(BundleItemValue{BundleItemSocketValue{socket_type, &value_variant}});
}
else {
fn(BundleItemValue{BundleItemSocketValue{socket_type, &value}});
}
auto value_variant = bke::SocketValueVariant::From(std::forward<T>(value));
fn(BundleItemValue{BundleItemSocketValue{socket_type, &value_variant}});
}
else {
/* All allowed types should be handled above already. */

View File

@@ -6,6 +6,7 @@
#include "BKE_geometry_set.hh"
#include "BKE_node.hh"
#include "BKE_node_socket_value.hh"
#include "BKE_volume_grid_fwd.hh"
#include "BLI_color.hh"
@@ -32,36 +33,12 @@ struct GeoNodesUserData;
namespace blender::nodes {
/** True if a static type can also exist as field in Geometry Nodes. */
template<typename T>
static constexpr bool geo_nodes_is_field_base_type_v = is_same_any_v<T,
float,
int,
bool,
ColorGeometry4f,
float3,
std::string,
math::Quaternion,
float4x4>;
/** True if Geometry Nodes sockets can store values of the given type and the type is stored
* embedded in a #SocketValueVariant. */
template<typename T>
static constexpr bool geo_nodes_type_stored_as_SocketValueVariant_v =
std::is_enum_v<T> || geo_nodes_is_field_base_type_v<T> || fn::is_field_v<T> ||
bke::is_VolumeGrid_v<T> ||
is_same_any_v<T,
fn::GField,
bke::GVolumeGrid,
nodes::BundlePtr,
nodes::ClosurePtr,
nodes::ListPtr,
Object *,
Collection *,
Tex *,
Image *,
Material *,
bke::GeometrySet>;
template<typename T> struct GeoNodesMultiInput {
using value_type = T;
Vector<T> values;
};
template<typename T> constexpr bool is_GeoNodesMultiInput_v = false;
template<typename T> constexpr bool is_GeoNodesMultiInput_v<GeoNodesMultiInput<T>> = true;
/**
* Executes a multi-function. If all inputs are single values, the results will also be single

View File

@@ -140,13 +140,10 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
Vector<SocketValueVariant> values = params.extract_input<Vector<SocketValueVariant>>("Mesh 2");
Vector<GeometrySet> geometry_sets;
for (SocketValueVariant &value : values) {
geometry_sets.append(value.extract<GeometrySet>());
}
GeoNodesMultiInput<GeometrySet> geometry_sets =
params.extract_input<GeoNodesMultiInput<GeometrySet>>("Mesh 2");
for (const GeometrySet &geometry : geometry_sets) {
for (const GeometrySet &geometry : geometry_sets.values) {
if (const Mesh *mesh = geometry.get_mesh()) {
meshes.append(mesh);
transforms.append(float4x4::identity());
@@ -251,7 +248,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Vector<GeometrySet> all_geometries;
all_geometries.append(set_a);
all_geometries.extend(geometry_sets);
all_geometries.extend(geometry_sets.values);
const std::array types_to_join = {GeometryComponent::Type::Edit};
GeometrySet result_geometry = geometry::join_geometries(

View File

@@ -18,12 +18,11 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
Vector<SocketValueVariant> input_values = params.extract_input<Vector<SocketValueVariant>>(
"Geometry");
GeoNodesMultiInput<GeometrySet> geometries =
params.extract_input<GeoNodesMultiInput<GeometrySet>>("Geometry");
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
for (bke::SocketValueVariant &value : input_values) {
bke::GeometrySet geometry = value.extract<bke::GeometrySet>();
for (GeometrySet &geometry : geometries.values) {
geometry.ensure_owns_direct_data();
const int handle = instances->add_reference(std::move(geometry));
instances->add_instance(handle, float4x4::identity());

View File

@@ -20,20 +20,16 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
Vector<SocketValueVariant> input_values = params.extract_input<Vector<SocketValueVariant>>(
"Geometry");
Vector<GeometrySet> geometry_sets;
for (SocketValueVariant &value : input_values) {
geometry_sets.append(value.extract<GeometrySet>());
}
GeoNodesMultiInput<GeometrySet> geometries =
params.extract_input<GeoNodesMultiInput<GeometrySet>>("Geometry");
const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Geometry");
for (GeometrySet &geometry : geometry_sets) {
for (GeometrySet &geometry : geometries.values) {
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry);
}
GeometrySet geometry_set_result = geometry::join_geometries(geometry_sets, attribute_filter);
GeometrySet geometry_set_result = geometry::join_geometries(geometries.values, attribute_filter);
params.set_output("Geometry", std::move(geometry_set_result));
}

View File

@@ -81,35 +81,23 @@ static void node_init(bNodeTree * /*tree*/, bNode *node)
node->custom1 = int16_t(Operation::Difference);
}
#ifdef WITH_OPENVDB
static void get_float_grids(MutableSpan<SocketValueVariant> values,
Vector<bke::VolumeGrid<float>> &grids)
{
for (SocketValueVariant &input : values) {
if (auto grid = input.extract<bke::VolumeGrid<float>>()) {
grids.append(std::move(grid));
}
}
}
#endif
static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
const Operation operation = Operation(params.node().custom1);
Vector<SocketValueVariant> inputs = params.extract_input<Vector<SocketValueVariant>>("Grid 2");
auto grids = params.extract_input<GeoNodesMultiInput<bke::VolumeGrid<float>>>("Grid 2");
Vector<bke::VolumeGrid<float>> operands;
switch (operation) {
case Operation::Intersect:
case Operation::Union:
get_float_grids(inputs, operands);
operands.extend(grids.values);
break;
case Operation::Difference:
if (auto grid = params.extract_input<bke::VolumeGrid<float>>("Grid 1")) {
operands.append(std::move(grid));
}
get_float_grids(inputs, operands);
operands.extend(grids.values);
break;
}

View File

@@ -17,13 +17,13 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
Vector<SocketValueVariant> strings = params.extract_input<Vector<SocketValueVariant>>("Strings");
auto strings = params.extract_input<GeoNodesMultiInput<std::string>>("Strings");
const std::string delim = params.extract_input<std::string>("Delimiter");
std::string output;
for (const int i : strings.index_range()) {
output += strings[i].extract<std::string>();
if (i < (strings.size() - 1)) {
for (const int i : strings.values.index_range()) {
output += strings.values[i];
if (i < (strings.values.size() - 1)) {
output += delim;
}
}

View File

@@ -87,15 +87,6 @@ static const CPPType *get_socket_cpp_type(const bNodeSocket &socket)
return get_socket_cpp_type(*socket.typeinfo);
}
static const CPPType *get_vector_type(const CPPType &type)
{
const VectorCPPType *vector_type = VectorCPPType::get_from_value(type);
if (vector_type == nullptr) {
return nullptr;
}
return &vector_type->self;
}
/**
* Checks which sockets of the node are available and creates corresponding inputs/outputs on the
* lazy-function.
@@ -116,7 +107,7 @@ static void lazy_function_interface_from_node(const bNode &node,
continue;
}
if (socket->is_multi_input() && !is_muted) {
type = get_vector_type(*type);
type = &CPPType::get<GeoNodesMultiInput<SocketValueVariant>>();
}
r_lf_index_by_bsocket[socket->index_in_tree()] = r_inputs.append_and_get_index_as(
socket->name, *type, input_usage);
@@ -326,17 +317,12 @@ class LazyFunctionForGeometryNode : public LazyFunction {
* multi-inputs are not supported in lazy-function graphs.
*/
class LazyFunctionForMultiInput : public LazyFunction {
private:
const CPPType *base_type_;
public:
Vector<const bNodeLink *> links;
LazyFunctionForMultiInput(const bNodeSocket &socket)
{
debug_name_ = "Multi Input";
base_type_ = get_socket_cpp_type(socket);
BLI_assert(base_type_ != nullptr);
BLI_assert(socket.is_multi_input());
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_muted() || !link->fromsock->is_available() ||
@@ -344,21 +330,18 @@ class LazyFunctionForMultiInput : public LazyFunction {
{
continue;
}
inputs_.append({"Input", *base_type_});
inputs_.append({"Input", CPPType::get<SocketValueVariant>()});
this->links.append(link);
}
const CPPType *vector_type = get_vector_type(*base_type_);
BLI_assert(vector_type != nullptr);
outputs_.append({"Output", *vector_type});
outputs_.append({"Output", CPPType::get<GeoNodesMultiInput<SocketValueVariant>>()});
}
void execute_impl(lf::Params &params, const lf::Context & /*context*/) const override
{
BLI_assert(base_type_ == &CPPType::get<SocketValueVariant>());
void *output_ptr = params.get_output_data_ptr(0);
auto &values = *new (output_ptr) Vector<SocketValueVariant>();
auto &values = *new (output_ptr) GeoNodesMultiInput<SocketValueVariant>();
for (const int i : inputs_.index_range()) {
values.append(params.extract_input<SocketValueVariant>(i));
values.values.append(params.extract_input<SocketValueVariant>(i));
}
params.output_set(0);
}

View File

@@ -172,8 +172,7 @@ void GeoNodeExecParams::set_default_remaining_outputs()
set_default_remaining_node_outputs(params_, node_);
}
void GeoNodeExecParams::check_input_access(StringRef identifier,
const CPPType *requested_type) const
void GeoNodeExecParams::check_input_access(StringRef identifier) const
{
const bNodeSocket *found_socket = nullptr;
for (const bNodeSocket *socket : node_.input_sockets()) {
@@ -199,17 +198,9 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
<< "' is disabled.\n";
BLI_assert_unreachable();
}
else if (requested_type != nullptr && (found_socket->flag & SOCK_MULTI_INPUT) == 0) {
const CPPType &expected_type = *found_socket->typeinfo->geometry_nodes_cpp_type;
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
BLI_assert_unreachable();
}
}
}
void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const
void GeoNodeExecParams::check_output_access(StringRef identifier) const
{
const bNodeSocket *found_socket = nullptr;
for (const bNodeSocket *socket : node_.output_sockets()) {
@@ -239,14 +230,6 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType
std::cout << "The identifier '" << identifier << "' has been set already.\n";
BLI_assert_unreachable();
}
else {
const CPPType &expected_type = *found_socket->typeinfo->geometry_nodes_cpp_type;
if (value_type != expected_type) {
std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
BLI_assert_unreachable();
}
}
}
AttributeFilter::Result NodeAttributeFilter::filter(const StringRef attribute_name) const