Page Menu
Home
Search
Configure Global Search
Log In
Paste
P2352
Mockup of some fields changes to geometry node evaluator
Active
Public
Actions
Authored by
Hans Goudey (HooglyBoogly)
on Aug 31 2021, 4:55 AM.
Edit Paste
Archive Paste
View Raw File
Subscribe
Mute Notifications
Award Token
Tags
Geometry Nodes
Subscribers
None
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)
created this paste.
Aug 31 2021, 4:55 AM
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
Hans Goudey (HooglyBoogly)
added a project:
Geometry Nodes
.
Hans Goudey (HooglyBoogly)
mentioned this in
T90875: Update geometry nodes evaluator to pass fields instead of raw data
.
Log In to Comment