Geometry Nodes: refactor how side effect nodes are set up

Previously, the geometry nodes modifier was converting the
viewer path to a compute context at the same time as it was
setting up side effect nodes for the geometry nodes evaluation.

Now, this is changed to be a two step process. First, the viewer
path is converted to the corresponding compute context.
Afterwards, a separate function sets side effect nodes up so
that the given node in the given compute context will be evaluated.

This has three main benefits:
* More obvious separation of concerns.
* Can reuse the code that maps a viewer path element to a compute
  context already.
* With gizmo nodes (#112677), it may become necessary to add side
  effect nodes  based on a compute context, but without having a
  corresponding viewer path.
This commit is contained in:
Jacques Lucke
2023-10-03 14:12:12 +02:00
parent 10ce3988e2
commit fb38327e6a
7 changed files with 207 additions and 143 deletions

View File

@@ -30,6 +30,11 @@ class ModifierComputeContext : public ComputeContext {
public:
ModifierComputeContext(const ComputeContext *parent, std::string modifier_name);
StringRefNull modifier_name() const
{
return modifier_name_;
}
private:
void print_current_in_line(std::ostream &stream) const override;
};

View File

@@ -6,6 +6,7 @@
#include <optional>
#include "BLI_compute_context.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
@@ -76,4 +77,13 @@ enum class UpdateActiveGeometryNodesViewerResult {
UpdateActiveGeometryNodesViewerResult update_active_geometry_nodes_viewer(const bContext &C,
ViewerPath &viewer_path);
/**
* Some viewer path elements correspond to compute-contexts. This function converts from the viewer
* path element to the corresponding compute context if possible.
*
* \return False, there is no matching compute context.
*/
[[nodiscard]] bool add_compute_context_for_viewer_path_elem(
const ViewerPathElem &elem, ComputeContextBuilder &compute_context_builder);
} // namespace blender::ed::viewer_path

View File

@@ -5,6 +5,7 @@
#include "ED_viewer_path.hh"
#include "ED_screen.hh"
#include "BKE_compute_contexts.hh"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_node_runtime.hh"
@@ -454,4 +455,37 @@ bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snod
return nullptr;
}
[[nodiscard]] bool add_compute_context_for_viewer_path_elem(
const ViewerPathElem &elem_generic, ComputeContextBuilder &compute_context_builder)
{
switch (ViewerPathElemType(elem_generic.type)) {
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
case VIEWER_PATH_ELEM_TYPE_ID: {
return false;
}
case VIEWER_PATH_ELEM_TYPE_MODIFIER: {
const auto &elem = reinterpret_cast<const ModifierViewerPathElem &>(elem_generic);
compute_context_builder.push<bke::ModifierComputeContext>(elem.modifier_name);
return true;
}
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
const auto &elem = reinterpret_cast<const GroupNodeViewerPathElem &>(elem_generic);
compute_context_builder.push<bke::NodeGroupComputeContext>(elem.node_id);
return true;
}
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
const auto &elem = reinterpret_cast<const SimulationZoneViewerPathElem &>(elem_generic);
compute_context_builder.push<bke::SimulationZoneComputeContext>(elem.sim_output_node_id);
return true;
}
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
const auto &elem = reinterpret_cast<const RepeatZoneViewerPathElem &>(elem_generic);
compute_context_builder.push<bke::RepeatZoneComputeContext>(elem.repeat_output_node_id,
elem.iteration);
return true;
}
}
return false;
}
} // namespace blender::ed::viewer_path

View File

@@ -517,6 +517,155 @@ const NodesModifierBake *NodesModifierData::find_bake(const int id) const
namespace blender {
/**
* Setup side effects nodes so that the given node in the given compute context will be executed.
* To make sure that it is executed, all parent group nodes and zones have to be set to have side
* effects as well.
*/
static void try_add_side_effect_node(const ComputeContext &final_compute_context,
const int final_node_id,
const NodesModifierData &nmd,
nodes::GeoNodesSideEffectNodes &r_side_effect_nodes)
{
if (nmd.node_group == nullptr) {
return;
}
Vector<const ComputeContext *> compute_context_vec;
for (const ComputeContext *c = &final_compute_context; c; c = c->parent()) {
compute_context_vec.append(c);
}
std::reverse(compute_context_vec.begin(), compute_context_vec.end());
const auto *modifier_compute_context = dynamic_cast<const bke::ModifierComputeContext *>(
compute_context_vec[0]);
if (modifier_compute_context == nullptr) {
return;
}
if (modifier_compute_context->modifier_name() != nmd.modifier.name) {
return;
}
const bNodeTree *current_tree = nmd.node_group;
const bke::bNodeTreeZone *current_zone = nullptr;
/* Write side effect nodes to a new map and only if everything succeeds, move the nodes to the
* caller. This is easier than changing r_side_effect_nodes directly and then undoing changes in
* case of errors. */
nodes::GeoNodesSideEffectNodes local_side_effect_nodes;
for (const ComputeContext *compute_context_generic : compute_context_vec.as_span().drop_front(1))
{
const bke::bNodeTreeZones *current_zones = current_tree->zones();
if (current_zones == nullptr) {
return;
}
const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*current_tree);
if (lf_graph_info == nullptr) {
return;
}
const ComputeContextHash &parent_compute_context_hash =
compute_context_generic->parent()->hash();
if (const auto *compute_context = dynamic_cast<const bke::SimulationZoneComputeContext *>(
compute_context_generic))
{
const bke::bNodeTreeZone *simulation_zone = current_zones->get_zone_by_node(
compute_context->output_node_id());
if (simulation_zone == nullptr) {
return;
}
if (simulation_zone->parent_zone != current_zone) {
return;
}
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
simulation_zone, nullptr);
if (lf_zone_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_zone_node);
current_zone = simulation_zone;
}
else if (const auto *compute_context = dynamic_cast<const bke::RepeatZoneComputeContext *>(
compute_context_generic))
{
const bke::bNodeTreeZone *repeat_zone = current_zones->get_zone_by_node(
compute_context->output_node_id());
if (repeat_zone == nullptr) {
return;
}
if (repeat_zone->parent_zone != current_zone) {
return;
}
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
repeat_zone, nullptr);
if (lf_zone_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_zone_node);
local_side_effect_nodes.iterations_by_repeat_zone.add(
{parent_compute_context_hash, compute_context->output_node_id()},
compute_context->iteration());
current_zone = repeat_zone;
}
else if (const auto *compute_context = dynamic_cast<const bke::NodeGroupComputeContext *>(
compute_context_generic))
{
const bNode *group_node = current_tree->node_by_id(compute_context->node_id());
if (group_node == nullptr) {
return;
}
if (group_node->id == nullptr) {
return;
}
if (group_node->is_muted()) {
return;
}
if (current_zone != current_zones->get_zone_by_node(group_node->identifier)) {
return;
}
const lf::FunctionNode *lf_group_node = lf_graph_info->mapping.group_node_map.lookup_default(
group_node, nullptr);
if (lf_group_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_group_node);
current_tree = reinterpret_cast<const bNodeTree *>(group_node->id);
current_zone = nullptr;
}
else {
return;
}
}
const bNode *final_node = current_tree->node_by_id(final_node_id);
if (final_node == nullptr) {
return;
}
const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*current_tree);
if (lf_graph_info == nullptr) {
return;
}
const bke::bNodeTreeZones *tree_zones = current_tree->zones();
if (tree_zones == nullptr) {
return;
}
if (tree_zones->get_zone_by_node(final_node_id) != current_zone) {
return;
}
const lf::FunctionNode *lf_node =
lf_graph_info->mapping.possible_side_effect_node_map.lookup_default(final_node, nullptr);
if (lf_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(final_compute_context.hash(), lf_node);
/* Successfully found all side effect nodes for the viewer path. */
for (const auto item : local_side_effect_nodes.nodes_by_context.items()) {
r_side_effect_nodes.nodes_by_context.add_multiple(item.key, item.value);
}
for (const auto item : local_side_effect_nodes.iterations_by_repeat_zone.items()) {
r_side_effect_nodes.iterations_by_repeat_zone.add_multiple(item.key, item.value);
}
}
static void find_side_effect_nodes_for_viewer_path(
const ViewerPath &viewer_path,
const NodesModifierData &nmd,
@@ -538,130 +687,15 @@ static void find_side_effect_nodes_for_viewer_path(
ComputeContextBuilder compute_context_builder;
compute_context_builder.push<bke::ModifierComputeContext>(parsed_path->modifier_name);
/* Write side effect nodes to a new map and only if everything succeeds, move the nodes to the
* caller. This is easier than changing r_side_effect_nodes directly and then undoing changes in
* case of errors. */
nodes::GeoNodesSideEffectNodes local_side_effect_nodes;
const bNodeTree *group = nmd.node_group;
const bke::bNodeTreeZone *zone = nullptr;
for (const ViewerPathElem *elem : parsed_path->node_path) {
const bke::bNodeTreeZones *tree_zones = group->zones();
if (tree_zones == nullptr) {
if (!ed::viewer_path::add_compute_context_for_viewer_path_elem(*elem, compute_context_builder))
{
return;
}
const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*group);
if (lf_graph_info == nullptr) {
return;
}
switch (elem->type) {
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
const auto &typed_elem = *reinterpret_cast<const SimulationZoneViewerPathElem *>(elem);
const bke::bNodeTreeZone *next_zone = tree_zones->get_zone_by_node(
typed_elem.sim_output_node_id);
if (next_zone == nullptr) {
return;
}
if (next_zone->parent_zone != zone) {
return;
}
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
next_zone, nullptr);
if (lf_zone_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(compute_context_builder.hash(), lf_zone_node);
compute_context_builder.push<bke::SimulationZoneComputeContext>(*next_zone->output_node);
zone = next_zone;
break;
}
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
const auto &typed_elem = *reinterpret_cast<const RepeatZoneViewerPathElem *>(elem);
const bke::bNodeTreeZone *next_zone = tree_zones->get_zone_by_node(
typed_elem.repeat_output_node_id);
if (next_zone == nullptr) {
return;
}
if (next_zone->parent_zone != zone) {
return;
}
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
next_zone, nullptr);
if (lf_zone_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(compute_context_builder.hash(), lf_zone_node);
local_side_effect_nodes.iterations_by_repeat_zone.add(
{compute_context_builder.hash(), typed_elem.repeat_output_node_id},
typed_elem.iteration);
compute_context_builder.push<bke::RepeatZoneComputeContext>(*next_zone->output_node,
typed_elem.iteration);
zone = next_zone;
break;
}
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
const auto &typed_elem = *reinterpret_cast<const GroupNodeViewerPathElem *>(elem);
const bNode *node = group->node_by_id(typed_elem.node_id);
if (node == nullptr) {
return;
}
if (node->id == nullptr) {
return;
}
if (node->is_muted()) {
return;
}
if (zone != tree_zones->get_zone_by_node(node->identifier)) {
return;
}
const lf::FunctionNode *lf_group_node =
lf_graph_info->mapping.group_node_map.lookup_default(node, nullptr);
if (lf_group_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(compute_context_builder.hash(),
lf_group_node);
compute_context_builder.push<bke::NodeGroupComputeContext>(*node);
group = reinterpret_cast<const bNodeTree *>(node->id);
zone = nullptr;
break;
}
default: {
BLI_assert_unreachable();
return;
}
}
}
const bNode *found_viewer_node = group->node_by_id(parsed_path->viewer_node_id);
if (found_viewer_node == nullptr) {
return;
}
const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*group);
if (lf_graph_info == nullptr) {
return;
}
const bke::bNodeTreeZones *tree_zones = group->zones();
if (tree_zones == nullptr) {
return;
}
if (tree_zones->get_zone_by_node(found_viewer_node->identifier) != zone) {
return;
}
const lf::FunctionNode *lf_viewer_node = lf_graph_info->mapping.viewer_node_map.lookup_default(
found_viewer_node, nullptr);
if (lf_viewer_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(compute_context_builder.hash(), lf_viewer_node);
/* Successfully found all side effect nodes for the viewer path. */
for (const auto item : local_side_effect_nodes.nodes_by_context.items()) {
r_side_effect_nodes.nodes_by_context.add_multiple(item.key, item.value);
}
for (const auto item : local_side_effect_nodes.iterations_by_repeat_zone.items()) {
r_side_effect_nodes.iterations_by_repeat_zone.add_multiple(item.key, item.value);
}
try_add_side_effect_node(
*compute_context_builder.current(), parsed_path->viewer_node_id, nmd, r_side_effect_nodes);
}
static void find_side_effect_nodes(const NodesModifierData &nmd,

View File

@@ -270,7 +270,7 @@ struct GeometryNodeLazyFunctionGraphMapping {
* types, so better have more specialized mappings for now.
*/
Map<const bNode *, const lf::FunctionNode *> group_node_map;
Map<const bNode *, const lf::FunctionNode *> viewer_node_map;
Map<const bNode *, const lf::FunctionNode *> possible_side_effect_node_map;
Map<const bke::bNodeTreeZone *, const lf::FunctionNode *> zone_node_map;
/* Indexed by #bNodeSocket::index_in_all_outputs. */

View File

@@ -3466,7 +3466,7 @@ struct GeometryNodesLazyFunctionBuilder {
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
}
mapping_->viewer_node_map.add(&bnode, &lf_viewer_node);
mapping_->possible_side_effect_node_map.add(&bnode, &lf_viewer_node);
{
auto &usage_lazy_function = scope_.construct<LazyFunctionForViewerInputUsage>(

View File

@@ -578,28 +578,9 @@ const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_path(const ViewerP
ComputeContextBuilder compute_context_builder;
compute_context_builder.push<bke::ModifierComputeContext>(parsed_path->modifier_name);
for (const ViewerPathElem *elem : parsed_path->node_path) {
switch (elem->type) {
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
const auto &typed_elem = *reinterpret_cast<const GroupNodeViewerPathElem *>(elem);
compute_context_builder.push<bke::NodeGroupComputeContext>(typed_elem.node_id);
break;
}
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
const auto &typed_elem = *reinterpret_cast<const SimulationZoneViewerPathElem *>(elem);
compute_context_builder.push<bke::SimulationZoneComputeContext>(
typed_elem.sim_output_node_id);
break;
}
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
const auto &typed_elem = *reinterpret_cast<const RepeatZoneViewerPathElem *>(elem);
compute_context_builder.push<bke::RepeatZoneComputeContext>(
typed_elem.repeat_output_node_id, typed_elem.iteration);
break;
}
default: {
BLI_assert_unreachable();
break;
}
if (!ed::viewer_path::add_compute_context_for_viewer_path_elem(*elem, compute_context_builder))
{
return nullptr;
}
}
const ComputeContextHash context_hash = compute_context_builder.hash();