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:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user