Page MenuHome
Paste P2352

Mockup of some fields changes to geometry node evaluator
ActivePublic

Authored by Hans Goudey (HooglyBoogly) on Aug 31 2021, 4:55 AM.
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index 38aefde5e9f..de8795f0787 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -119,9 +119,10 @@ class Field {
*/
class FieldFunction {
/**
- * The function used to calculate the
+ * The function used to calculate the output(s).
*/
- std::unique_ptr<MultiFunction> function_;
+ std::unique_ptr<MultiFunction> owned_function_;
+ const MultiFunction *function_;
/**
* References to descriptions of the results from the functions this function depends on.
@@ -130,8 +131,19 @@ class FieldFunction {
public:
FieldFunction(std::unique_ptr<MultiFunction> function, Vector<Field> &&inputs)
- : function_(std::move(function)), inputs_(std::move(inputs))
+ : owned_function_(std::move(function)),
+ inputs_(std::move(inputs)),
+ function_(owned_function_.get())
{
+ BLI_assert(function_ != nullptr);
+ }
+ /**
+ * \warning: The function pointed to by #function must outlive the FieldFunction.
+ */
+ FieldFunction(const MultiFunction &function, Vector<Field> &&inputs)
+ : function_(&function), inputs_(std::move(inputs))
+ {
+ BLI_assert(function_ != nullptr);
}
Span<Field> inputs() const
@@ -162,6 +174,48 @@ class FieldInput {
}
};
+class ConstantFieldInput final : public FieldInput {
+ GPointer value_;
+
+ public:
+ ConstantFieldInput(const CPPType &type, const void *value, StringRef name = "")
+ : FieldInput(name), value_(type, &value)
+ {
+ BLI_assert(value != nullptr);
+ }
+
+ /**
+ * \warning: Does not take ownership of the value, expects the lifetime
+ * of the pointed to value to be longer than the field.
+ */
+ template<typename T>
+ ConstantFieldInput(const T &value, StringRef name = "")
+ : ConstantFieldInput(CPPType::get<T>, &value, name)
+ {
+ }
+
+ GVArrayPtr get_varray_generic_context(IndexMask mask) const final
+ {
+ return std::make_unique<GVArray_For_SingleValueRef>(
+ *value_.type(), mask.min_array_size(), value_.get());
+ }
+};
+
+class DefaultValueFieldInput final : public FieldInput {
+ const CPPType *type_;
+
+ public:
+ DefaultValueFieldInput(const CPPType &type, StringRef name = "") : FieldInput(name), type_(&type)
+ {
+ }
+
+ GVArrayPtr get_varray_generic_context(IndexMask mask) const final
+ {
+ return std::make_unique<GVArray_For_SingleValue>(
+ type_, mask.min_array_size(), type_->default_value());
+ }
+};
+
/**
* Evaluate more than one field at a time, as an optimization
* in case they share inputs or various intermediate values.
@@ -170,4 +224,15 @@ void evaluate_fields(blender::Span<Field> fields,
blender::IndexMask mask,
blender::Span<GMutableSpan> outputs);
-} // namespace blender::fn
\ No newline at end of file
+} // namespace blender::fn
+
+// #define MAKE_FIELD_REF_CPP_TYPE(DEBUG_NAME, FIELD_TYPE)
+// template<>
+// const blender::fn::CPPType &
+// blender::fn::CPPType::get_impl<blender::bke::FieldRef<FIELD_TYPE>>()
+// {
+// static blender::bke::FieldRefCPPType cpp_type{
+// blender::bke::FieldRefCPPTypeParam<blender::bke::FieldRef<FIELD_TYPE>>(),
+// STRINGIFY(DEBUG_NAME)};
+// return cpp_type;
+// }
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index 5646e37707c..801f05ccbc0 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -21,6 +21,8 @@
#include "DEG_depsgraph_query.h"
+#include "FN_cpp_type_make.hh"
+#include "FN_field.hh"
#include "FN_generic_value_map.hh"
#include "FN_multi_function.hh"
@@ -30,6 +32,8 @@
#include "BLI_task.hh"
#include "BLI_vector_set.hh"
+MAKE_CPP_TYPE(Field, fn::Field, CPPTypeFlags::None);
+
namespace blender::modifiers::geometry_nodes {
using fn::CPPType;
@@ -858,11 +862,10 @@ class GeometryNodesEvaluator {
const MultiFunction &fn,
NodeState &node_state)
{
- MFContextBuilder fn_context;
- MFParamsBuilder fn_params{fn, 1};
LinearAllocator<> &allocator = local_allocators_.local();
/* Prepare the inputs for the multi function. */
+ Vector<fn::Field> input_fields;
for (const int i : node->inputs().index_range()) {
const InputSocketRef &socket_ref = node->input(i);
if (!socket_ref.is_available()) {
@@ -873,7 +876,10 @@ class GeometryNodesEvaluator {
BLI_assert(input_state.was_ready_for_execution);
SingleInputValue &single_value = *input_state.value.single;
BLI_assert(single_value.value != nullptr);
- fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value});
+ /* TODO: This probably won't work if the input node outputted a field. */
+ input_fields.append(fn::Field(
+ *input_state.type,
+ std::make_shared<fn::ConstantFieldInput>(*input_state.type, single_value.value)));
}
/* Prepare the outputs for the multi function. */
Vector<GMutablePointer> outputs;
@@ -882,13 +888,10 @@ class GeometryNodesEvaluator {
if (!socket_ref.is_available()) {
continue;
}
- const CPPType &type = *get_socket_cpp_type(socket_ref);
- void *buffer = allocator.allocate(type.size(), type.alignment());
- fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1});
- outputs.append({type, buffer});
}
- fn.call(IndexRange(1), fn_params, fn_context);
+ std::shared_ptr<fn::FieldFunction> field_function = std::make_shared<fn::FieldFunction>(
+ fn, std::move(input_fields));
/* Forward the computed outputs. */
int output_index = 0;
@@ -899,7 +902,10 @@ class GeometryNodesEvaluator {
}
OutputState &output_state = node_state.outputs[i];
const DOutputSocket socket{node.context(), &socket_ref};
- GMutablePointer value = outputs[output_index];
+
+ const CPPType &type = *get_socket_cpp_type(socket_ref);
+ destruct_ptr<fn::Field> field = allocator.construct<fn::Field>(type, field_function, i);
+ GMutablePointer value{CPPType::get<fn::Field>(), field.get()};
this->forward_output(socket, value);
output_state.has_been_computed = true;
output_index++;
@@ -1363,40 +1369,53 @@ class GeometryNodesEvaluator {
}
}
- GMutablePointer get_value_from_socket(const DSocket socket, const CPPType &required_type)
+ fn::Field get_value_from_socket(const DSocket socket, const CPPType &required_type)
{
LinearAllocator<> &allocator = local_allocators_.local();
const CPPType &type = *get_socket_cpp_type(socket);
void *buffer = allocator.allocate(type.size(), type.alignment());
- get_socket_value(*socket.socket_ref(), buffer);
+ fn::Field field = get_socket_value(*socket.socket_ref(), buffer);
- if (type == required_type) {
- return {type, buffer};
+ if (field.type() == required_type) {
+ return field;
}
- void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment());
- this->convert_value(type, required_type, buffer, converted_buffer);
- return {required_type, converted_buffer};
+
+ return this->convert_value(field, required_type);
}
- void convert_value(const CPPType &from_type,
- const CPPType &to_type,
- const void *from_value,
- void *to_value)
+ fn::Field convert_value(const fn::Field &from_field, const CPPType &to_type)
{
- if (from_type == to_type) {
- from_type.copy_construct(from_value, to_value);
- return;
+ if (from_field.type() == to_type) {
+ return from_field;
}
- if (conversions_.is_convertible(from_type, to_type)) {
- /* Do the conversion if possible. */
- conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value);
- }
- else {
- /* Cannot convert, use default value instead. */
- to_type.copy_construct(to_type.default_value(), to_value);
+ /* TODO: This wouldn't work at all with current architecture! If fields were just passed as the
+ * fn::Field CPPType, then we wouldn't be able to distinguish any of the types here. So we will
+ * actually need separate CPPTypes for fields for different socket types. That's not exactly
+ * what I was hoping though, since I liked the idea of the CPPType embedded in `fn::Field`, and
+ * I really liked the fact that it wasn't a virtual class-- every virtual part of the system
+ * was in the "nodes", i.e. the FieldInput and FieldFunction. It would be nice if fields could
+ * just be very simple references to those.
+ *
+ * Is every socket supposed to be passed as a field? If so, I think we could make some changes/
+ * simplifications to the entire evaluator, rather than trying to fit fields into the "pass by
+ * value" CPPType system here. */
+ if (conversions_.is_convertible(from_field.type(), to_type)) {
+ const CPPType &from_type = from_field.type();
+ const MultiFunction &fn = *conversions_.get_conversion_multi_function(
+ MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
+
+ std::shared_ptr<fn::FieldFunction> convert_fn = std::make_shared<fn::FieldFunction>(
+ fn, {from_field});
+
+ return fn::Field(to_type, convert_fn, 0);
}
+
+ /* Cannot convert, use default value instead. */
+ std::shared_ptr<fn::DefaultValueFieldInput> default_input =
+ std::make_shared<fn::DefaultValueFieldInput>();
+ return fn::Field(to_type, default_input);
}
NodeState &get_node_state(const DNode node)

Event Timeline

Hans Goudey (HooglyBoogly) changed the title of this paste from Command-Line Input to Mockup of some fields changes to geometry node evaluator.Aug 31 2021, 5:00 AM