[Bf-blender-cvs] [44e4f077a9d] master: Geometry Nodes: Run nodes once on unique instance data

Hans Goudey noreply at git.blender.org
Tue Sep 28 18:36:39 CEST 2021


Commit: 44e4f077a9d7b5b609f6199874802675d75f7266
Author: Hans Goudey
Date:   Tue Sep 28 11:36:28 2021 -0500
Branches: master
https://developer.blender.org/rB44e4f077a9d7b5b609f6199874802675d75f7266

Geometry Nodes: Run nodes once on unique instance data

As described in T91672, often it can be much more efficient to run each
node only on the unique geometry of the instances, rather than realizing
all instances and potentially processing redundant data. Sometimes the
performance difference can be completely smooth vs. completely unusable.

Geometry nodes used to hide that choice from users by always realizing
instances, but recently we have decided to expose it. So this commit
makes nodes run once per unique reference in the entire tree of nested
instances in their input geometries, continuing the work started in
rB0559971ab377 and rBf94164d89629f0d2. For the old behavior, a realize
instances node can be added before the nodes, which is done in the
versioning code.

Differential Revision: https://developer.blender.org/D12656

===================================================================

M	source/blender/blenkernel/BKE_blender_version.h
M	source/blender/blenkernel/BKE_geometry_set.hh
M	source/blender/blenkernel/intern/geometry_set.cc
M	source/blender/blenloader/intern/versioning_300.c
M	source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
M	source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
M	source/blender/nodes/geometry/nodes/node_geo_boolean.cc
M	source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
M	source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
M	source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
M	source/blender/nodes/geometry/nodes/node_geo_triangulate.cc

===================================================================

diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index b973d8b9a18..5ef0459663b 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
 
 /* Blender file format version. */
 #define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 27
+#define BLENDER_FILE_SUBVERSION 28
 
 /* Minimum Blender version that supports reading file written with the current
  * version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 4954b1d5ab2..724ca224cab 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -330,6 +330,7 @@ struct GeometrySet {
   bool has_volume() const;
   bool has_curve() const;
   bool has_realized_data() const;
+  bool is_empty() const;
 
   const Mesh *get_mesh_for_read() const;
   const PointCloud *get_pointcloud_for_read() const;
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 400e0fda518..0aac6ae3adf 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -318,6 +318,16 @@ bool GeometrySet::has_realized_data() const
   return this->get_component_for_read<InstancesComponent>() == nullptr;
 }
 
+/* Return true if the geometry set has any component that isn't empty. */
+bool GeometrySet::is_empty() const
+{
+  if (components_.is_empty()) {
+    return true;
+  }
+  return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() ||
+           this->has_instances());
+}
+
 /* Create a new geometry set that only contains the given mesh. */
 GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
 {
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 86755cbaf95..3b51e40c218 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -449,6 +449,73 @@ static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const Lis
 #undef SEQ_SPEED_COMPRESS_IPO_Y
 }
 
+static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket)
+{
+  LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+    if (link->tosock == in_socket) {
+      return link;
+    }
+  }
+  return NULL;
+}
+
+static void add_realize_instances_before_socket(bNodeTree *ntree,
+                                                bNode *node,
+                                                bNodeSocket *geometry_socket)
+{
+  BLI_assert(geometry_socket->type == SOCK_GEOMETRY);
+  bNodeLink *link = find_connected_link(ntree, geometry_socket);
+  if (link == NULL) {
+    return;
+  }
+
+  /* If the realize instances node is already before this socket, no need to continue. */
+  if (link->fromnode->type == GEO_NODE_REALIZE_INSTANCES) {
+    return;
+  }
+
+  bNode *realize_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_REALIZE_INSTANCES);
+  realize_node->parent = node->parent;
+  realize_node->locx = node->locx - 100;
+  realize_node->locy = node->locy;
+  nodeAddLink(ntree, link->fromnode, link->fromsock, realize_node, realize_node->inputs.first);
+  link->fromnode = realize_node;
+  link->fromsock = realize_node->outputs.first;
+}
+
+/**
+ * If a node used to realize instances implicitly and will no longer do so in 3.0, add a "Realize
+ * Instances" node in front of it to avoid changing behavior. Don't do this if the node will be
+ * replaced anyway though.
+ */
+static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree)
+{
+  LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
+    if (ELEM(node->type,
+             GEO_NODE_ATTRIBUTE_CAPTURE,
+             GEO_NODE_SEPARATE_COMPONENTS,
+             GEO_NODE_CONVEX_HULL,
+             GEO_NODE_CURVE_LENGTH,
+             GEO_NODE_BOOLEAN,
+             GEO_NODE_CURVE_FILLET,
+             GEO_NODE_CURVE_RESAMPLE,
+             GEO_NODE_CURVE_TO_MESH,
+             GEO_NODE_CURVE_TRIM,
+             GEO_NODE_MATERIAL_REPLACE,
+             GEO_NODE_MESH_SUBDIVIDE,
+             GEO_NODE_ATTRIBUTE_REMOVE,
+             GEO_NODE_TRIANGULATE)) {
+      bNodeSocket *geometry_socket = node->inputs.first;
+      add_realize_instances_before_socket(ntree, node, geometry_socket);
+    }
+    /* Also realize instances for the profile input of the curve to mesh node. */
+    if (node->type == GEO_NODE_CURVE_TO_MESH) {
+      bNodeSocket *profile_socket = node->inputs.last;
+      add_realize_instances_before_socket(ntree, node, profile_socket);
+    }
+  }
+}
+
 void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
 {
   if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
@@ -531,6 +598,14 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
     }
   }
 
+  if (!MAIN_VERSION_ATLEAST(bmain, 300, 28)) {
+    LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+      if (ntree->type == NTREE_GEOMETRY) {
+        version_geometry_nodes_add_realize_instance_nodes(ntree);
+      }
+    }
+  }
+
   /**
    * Versioning code until next subversion bump goes here.
    *
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index 81629a0f8b4..43fb00a482c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -117,8 +117,6 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
 {
   GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
 
-  geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
   const bNode &node = params.node();
   const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *)
                                                      node.storage;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index 21a9a338857..f93ef6f1db3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -47,8 +47,6 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
   GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
   Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute");
 
-  geometry_set = geometry_set_realize_instances(geometry_set);
-
   if (geometry_set.has<MeshComponent>()) {
     remove_attribute(
         geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 2a1c43a89fe..21b425c0ed4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -83,10 +83,14 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
   GeometrySet set_a;
   if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
     set_a = params.extract_input<GeometrySet>("Geometry 1");
+    if (set_a.has_instances()) {
+      params.error_message_add(
+          NodeWarningType::Info,
+          TIP_("Instances are not supported for the first geometry input, and will not be used"));
+    }
     /* Note that it technically wouldn't be necessary to realize the instances for the first
      * geometry input, but the boolean code expects the first shape for the difference operation
      * to be a single mesh. */
-    set_a = geometry_set_realize_instances(set_a);
     const Mesh *mesh_in_a = set_a.get_mesh_for_read();
     if (mesh_in_a != nullptr) {
       meshes.append(mesh_in_a);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index f1e10e3d276..4377d32210d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -227,9 +227,13 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
   return hull_from_bullet(geometry_set.get_mesh_for_read(), positions);
 }
 
+/* Since only positions are read from the instances, this can be used as an internal optimization
+ * to avoid the cost of realizing instances before the node. But disable this for now, since
+ * re-enabling that optimization will be a separate step. */
+#  if 0
 static void read_positions(const GeometryComponent &component,
-                           Span<float4x4> transforms,
-                           Vector<float3> *r_coords)
+                                           Span<float4x4> transforms,
+                                           Vector<float3> *r_coords)
 {
   GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
       "position", ATTR_DOMAIN_POINT, {0, 0, 0});
@@ -265,6 +269,31 @@ static void read_curve_positions(const CurveEval &curve,
   }
 }
 
+static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set)
+{
+  Vector<GeometryInstanceGroup> set_groups;
+  bke::geometry_set_gather_instances(geometry_set, set_groups);
+
+  Vector<float3> coords;
+
+  for (const GeometryInstanceGroup &set_group : set_groups) {
+    const GeometrySet &set = set_group.geometry_set;
+    Span<float4x4> transforms = set_group.transforms;
+
+    if (set.has_pointcloud()) {
+      read_positions(*set.get_component_for_read<PointCloudComponent>(), transforms, &coords);
+    }
+    if (set.has_mesh()) {
+      read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
+    }
+    if (set.has_curve()) {
+      read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
+    }
+  }
+  return hull_from_bullet(nullptr, coords);
+}
+#  endif
+
 #endif 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list