WIP: Closures and deferred evaluation for geometry nodes #107842

Draft
Lukas Tönne wants to merge 35 commits from LukasTonne/blender:geometry-nodes-closures into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
11 changed files with 191 additions and 25 deletions
Showing only changes of commit 034f3deaf0 - Show all commits

View File

@ -694,6 +694,12 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
break;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction &default_value =
*sock->default_value_typed<bNodeSocketValueFunction>();
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
break;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -704,7 +710,6 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_FUNCTION:
break;
}
}
@ -844,13 +849,15 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case SOCK_MATERIAL:
BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value);
break;
case SOCK_FUNCTION:
BLO_write_struct(writer, bNodeSocketValueFunction, sock->default_value);
break;
case SOCK_CUSTOM:
/* Custom node sockets where default_value is defined uses custom properties for storage. */
break;
case __SOCK_MESH:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_FUNCTION:
BLI_assert_unreachable();
break;
}
@ -1319,6 +1326,11 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
reader, lib, &sock->default_value_typed<bNodeSocketValueMaterial>()->value);
break;
}
case SOCK_FUNCTION: {
BLO_read_id_address(
reader, lib, &sock->default_value_typed<bNodeSocketValueFunction>()->value);
break;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -1329,7 +1341,6 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_FUNCTION:
break;
}
}
@ -1413,6 +1424,10 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
BLO_expand(expander, sock->default_value_typed<bNodeSocketValueMaterial>()->value);
break;
}
case SOCK_FUNCTION: {
BLO_expand(expander, sock->default_value_typed<bNodeSocketValueFunction>()->value);
break;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -1423,7 +1438,6 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_FUNCTION:
break;
}
}
@ -2070,6 +2084,12 @@ static void socket_id_user_increment(bNodeSocket *sock)
id_us_plus(reinterpret_cast<ID *>(default_value.value));
break;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction &default_value =
*sock->default_value_typed<bNodeSocketValueFunction>();
id_us_plus(reinterpret_cast<ID *>(default_value.value));
break;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -2080,7 +2100,6 @@ static void socket_id_user_increment(bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_FUNCTION:
break;
}
}
@ -2117,6 +2136,12 @@ static bool socket_id_user_decrement(bNodeSocket *sock)
id_us_min(reinterpret_cast<ID *>(default_value.value));
return default_value.value != nullptr;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction &default_value =
*sock->default_value_typed<bNodeSocketValueFunction>();
id_us_min(reinterpret_cast<ID *>(default_value.value));
return default_value.value != nullptr;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -2127,7 +2152,6 @@ static bool socket_id_user_decrement(bNodeSocket *sock)
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
case SOCK_FUNCTION:
break;
}
return false;
@ -2935,6 +2959,8 @@ static void *socket_value_storage(bNodeSocket &socket)
return &socket.default_value_typed<bNodeSocketValueObject>()->value;
case SOCK_MATERIAL:
return &socket.default_value_typed<bNodeSocketValueMaterial>()->value;
case SOCK_FUNCTION:
return &socket.default_value_typed<bNodeSocketValueFunction>()->value;
case SOCK_STRING:
/* We don't want do this now! */
return nullptr;
@ -2992,7 +3018,8 @@ void node_socket_move_default_value(Main & /*bmain*/,
SOCK_IMAGE,
SOCK_MATERIAL,
SOCK_TEXTURE,
SOCK_OBJECT))
SOCK_OBJECT,
SOCK_FUNCTION))
{
src_type.value_initialize(src_value);
}

View File

@ -915,7 +915,8 @@ void DepsgraphNodeBuilder::build_object_modifiers(Object *object)
}
if (modifier_node->flag & DEPSOP_FLAG_USER_MODIFIED) {
if (nmd->simulation_cache &&
nmd->simulation_cache->cache_state() == bke::sim::CacheState::Valid) {
nmd->simulation_cache->cache_state() == bke::sim::CacheState::Valid)
{
nmd->simulation_cache->invalidate();
}
}
@ -1767,6 +1768,9 @@ void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket)
else if (socket->type == SOCK_MATERIAL) {
build_id((ID *)((bNodeSocketValueMaterial *)socket->default_value)->value);
}
else if (socket->type == SOCK_FUNCTION) {
build_id((ID *)((bNodeSocketValueFunction *)socket->default_value)->value);
}
}
void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)

View File

@ -2647,6 +2647,12 @@ void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket)
build_material(material);
}
}
else if (socket->type == SOCK_FUNCTION) {
bNodeTree *ntree = ((bNodeSocketValueFunction *)socket->default_value)->value;
if (ntree != nullptr) {
build_nodetree(ntree);
}
}
}
void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)

View File

@ -1294,7 +1294,8 @@ static void std_node_socket_draw(
}
if ((sock->in_out == SOCK_OUT) || (sock->flag & SOCK_IS_LINKED) ||
(sock->flag & SOCK_HIDE_VALUE)) {
(sock->flag & SOCK_HIDE_VALUE))
{
node_socket_button_label(C, layout, ptr, node_ptr, text);
return;
}
@ -1416,6 +1417,10 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
case SOCK_FUNCTION: {
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
@ -1458,7 +1463,8 @@ static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, P
case SOCK_COLLECTION:
case SOCK_IMAGE:
case SOCK_TEXTURE:
case SOCK_MATERIAL: {
case SOCK_MATERIAL:
case SOCK_FUNCTION: {
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0);
break;
}

View File

@ -756,6 +756,10 @@ typedef struct bNodeSocketValueMaterial {
struct Material *value;
} bNodeSocketValueMaterial;
typedef struct bNodeSocketValueFunction {
struct bNodeTree *value;
} bNodeSocketValueFunction;
/* Data structs, for `node->storage`. */
typedef enum CMPNodeMaskType {

View File

@ -4865,6 +4865,20 @@ bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerR
return ma->gp_style == NULL;
}
bool rna_NodeSocketFunction_default_value_poll(PointerRNA *ptr, PointerRNA value)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNodeTree *ngroup = value.data;
/* only allow node trees of the same type as the group node's tree */
if (ngroup->type != ntree->type) {
return false;
}
const char *disabled_hint = NULL;
return nodeGroupPoll(ntree, ngroup, &disabled_hint);
}
static int rna_NodeConvertColorSpace_from_color_space_get(struct PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
@ -12666,14 +12680,40 @@ static void rna_def_node_socket_function(BlenderRNA *brna,
const char *interface_idname)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
RNA_def_struct_ui_text(srna, "Function Node Socket", "Function socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value");
prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "value");
RNA_def_property_struct_type(prop, "NodeTree");
RNA_def_property_pointer_funcs(
prop, NULL, NULL, NULL, "rna_NodeSocketFunction_default_value_poll");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
RNA_def_struct_ui_text(srna, "Function Node Socket Interface", "Function socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value");
prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "value");
RNA_def_property_struct_type(prop, "NodeTree");
RNA_def_property_pointer_funcs(
prop, NULL, NULL, NULL, "rna_NodeSocketFunction_default_value_poll");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void rna_def_node_socket_standard_types(BlenderRNA *brna)

View File

@ -85,6 +85,7 @@
#include "ED_undo.h"
#include "ED_viewer_path.hh"
#include "NOD_closure.hh"
#include "NOD_geometry.h"
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_node_declaration.hh"
@ -144,6 +145,12 @@ static void add_used_ids_from_sockets(const ListBase &sockets, Set<ID *> &ids)
}
break;
}
case SOCK_FUNCTION: {
if (bNodeTree *ntree = ((bNodeSocketValueFunction *)socket->default_value)->value) {
ids.add(&ntree->id);
}
break;
}
}
}
}
@ -508,6 +515,11 @@ static std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_c
socket.default_value);
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_FUNCTION: {
const bNodeSocketValueFunction *value = static_cast<const bNodeSocketValueFunction *>(
socket.default_value);
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
}
return nullptr;
}
@ -532,6 +544,7 @@ static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDP
case SOCK_TEXTURE:
case SOCK_IMAGE:
case SOCK_MATERIAL:
case SOCK_FUNCTION:
return property.type == IDP_ID;
}
BLI_assert_unreachable();
@ -611,6 +624,14 @@ static void init_socket_cpp_value_from_property(const IDProperty &property,
*(Material **)r_value = material;
break;
}
case SOCK_FUNCTION: {
using Closure = blender::nodes::Closure;
ID *id = IDP_Id(&property);
bNodeTree *ntree = (id && GS(id->name) == ID_NT) ? (bNodeTree *)id : nullptr;
new (r_value) Closure(Closure::make_from_node_tree(ntree));
break;
}
default: {
BLI_assert_unreachable();
break;
@ -1792,6 +1813,10 @@ static void draw_property_for_socket(const bContext &C,
uiItemPointerR(row, md_ptr, rna_path, bmain_ptr, "images", socket.name, ICON_IMAGE);
break;
}
case SOCK_FUNCTION: {
uiItemPointerR(row, md_ptr, rna_path, bmain_ptr, "node_groups", socket.name, ICON_NODETREE);
break;
}
default: {
if (input_has_attribute_toggle(*nmd->node_group, socket_index)) {
add_attribute_search_or_value_buttons(C, row, *nmd, md_ptr, socket);

View File

@ -6,9 +6,10 @@
* \ingroup nodes
*/
#include "BLI_cpp_type.hh"
#include "BLI_generic_pointer.hh"
struct bNodeTree;
namespace blender::nodes {
struct GeometryNodesLazyFunctionGraphInfo;
@ -31,11 +32,11 @@ class Closure {
std::shared_ptr<ClosureInputValues> bound_values_;
public:
Closure() = default;
Closure(const Closure &other) = default;
Closure();
Closure(const Closure &other);
explicit Closure(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
MutableSpan<GMutablePointer> &&bound_values);
~Closure();
~Closure() = default;
Closure &operator=(const Closure &other) = default;
@ -43,6 +44,8 @@ class Closure {
Span<GMutablePointer> bound_values() const;
MutableSpan<GMutablePointer> bound_values();
static Closure make_from_node_tree(const bNodeTree *node_tree);
};
} // namespace blender::nodes

View File

@ -6,6 +6,10 @@
#include "NOD_closure.hh"
#include "DNA_node_types.h"
#include "BKE_node_runtime.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
namespace blender::nodes {
@ -15,8 +19,10 @@ ClosureInputValues::ClosureInputValues(const Span<GMutablePointer> &values) : va
ClosureInputValues::~ClosureInputValues()
{
for (GMutablePointer &ptr : values_) {
ptr.destruct();
MEM_freeN(ptr.get());
if (ptr.get()) {
ptr.destruct();
MEM_freeN(ptr.get());
}
}
}
@ -30,6 +36,13 @@ MutableSpan<GMutablePointer> ClosureInputValues::values()
return values_;
}
Closure::Closure() : bound_values_(nullptr) {}
Closure::Closure(const Closure &other)
: lf_graph_info_(other.lf_graph_info_), bound_values_(other.bound_values_)
{
}
Closure::Closure(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
MutableSpan<GMutablePointer> &&bound_values)
: lf_graph_info_(&lf_graph_info),
@ -37,8 +50,6 @@ Closure::Closure(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
{
}
Closure::~Closure() {}
const GeometryNodesLazyFunctionGraphInfo *Closure::lf_graph_info() const
{
return lf_graph_info_;
@ -54,4 +65,21 @@ MutableSpan<GMutablePointer> Closure::bound_values()
return bound_values_->values();
}
Closure Closure ::make_from_node_tree(const bNodeTree *node_tree)
{
if (node_tree == nullptr) {
return Closure();
}
BLI_assert(node_tree->runtime);
const std::unique_ptr<blender::nodes::GeometryNodesLazyFunctionGraphInfo> &lf_graph_info_ptr =
node_tree->runtime->geometry_nodes_lazy_function_graph_info;
BLI_assert(lf_graph_info_ptr);
Array<GMutablePointer> bound_values(node_tree->interface_inputs().size());
// TODO fill with default values of the tree
return Closure(*lf_graph_info_ptr.get(), bound_values);
}
} // namespace blender::nodes

View File

@ -256,9 +256,12 @@ SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &ntree,
dst = std::move(value);
break;
}
case SOCK_FUNCTION:
dst = std::make_unique<decl::Function>();
case SOCK_FUNCTION: {
auto value = std::make_unique<decl::Function>();
value->default_value_fn = get_default_id_getter(ntree, io_socket);
dst = std::move(value);
break;
}
case SOCK_CUSTOM:
std::unique_ptr<decl::Custom> decl = std::make_unique<decl::Custom>();
decl->idname_ = io_socket.idname;
@ -368,7 +371,8 @@ void node_function_signature_declare(const bNodeFunctionSignature &sig,
r_declaration.inputs.append(std::move(decl));
}
for (const int socket_i : IndexRange(sig.outputs_num)) {
SocketDeclarationPtr decl = declaration_for_signature_parameter(sig.outputs[socket_i], SOCK_OUT);
SocketDeclarationPtr decl = declaration_for_signature_parameter(sig.outputs[socket_i],
SOCK_OUT);
if (field_interface) {
decl->output_field_dependency = field_interface->outputs[socket_i];
}
@ -543,7 +547,8 @@ bool BKE_node_is_connected_to_output(const bNodeTree *ntree, const bNode *node)
for (const bNodeSocket *socket : next_node->output_sockets()) {
for (const bNodeLink *link : socket->directly_linked_links()) {
if (link->tonode->typeinfo->nclass == NODE_CLASS_OUTPUT &&
link->tonode->flag & NODE_DO_OUTPUT) {
link->tonode->flag & NODE_DO_OUTPUT)
{
return true;
}
nodes_to_check.push(link->tonode);

View File

@ -418,6 +418,14 @@ void node_socket_init_default_value(bNodeSocket *sock)
"node socket value material");
dval->value = nullptr;
sock->default_value = dval;
break;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction *dval = MEM_cnew<bNodeSocketValueFunction>(
"node socket value function");
dval->value = nullptr;
sock->default_value = dval;
break;
}
@ -514,6 +522,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
id_us_plus(&toval->value->id);
break;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction *toval = (bNodeSocketValueFunction *)to->default_value;
bNodeSocketValueFunction *fromval = (bNodeSocketValueFunction *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
}
to->flag |= (from->flag & SOCK_HIDE_VALUE);
@ -796,10 +811,13 @@ static bNodeSocketType *make_socket_type_material()
static bNodeSocketType *make_socket_type_function()
{
using Closure = blender::nodes::Closure;
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FUNCTION, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<blender::nodes::Closure>();
socktype->get_base_cpp_value = [](const bNodeSocket &/*socket*/, void *r_value) {
new (r_value) blender::nodes::Closure();
socktype->base_cpp_type = &blender::CPPType::get<Closure>();
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
const bNodeTree *ntree = ((bNodeSocketValueFunction *)socket.default_value)->value;
new (r_value) Closure(Closure::make_from_node_tree(ntree));
};
socktype->geometry_nodes_cpp_type = socktype->base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;