Core: add concept of invariants in original DNA data

This patch adds a new `BKE_main_ensure_invariants` function. For now it only
ensures node-tree related invariants, but more may be added over time.

The already existing `ED_node_tree_propagate_change` now internally calls
`BKE_main_ensure_invariants`. We can probably remove this indirection at some
point and call the new function directly, but for now it is kept to keep this
patch small.

This is based on a recent discussion in the Core module meeting:
https://devtalk.blender.org/t/2024-12-12-core-meeting/38074

```cpp
/**
 * Makes sure that invariants in original DNA data are maintained after changes.
 *
 * This function has to be idempotent, i.e. after calling it once, additional calls should not
 * modify DNA data further. If it would, it would imply that this function does more than
 * maintaining invariants.
 *
 * This has to be called after any kind of change to original DNA data that may be involved in some
 * of the maintained invariants. It's possible to do multiple changes in a row and then fixing all
 * invariants with a single call in the end. Obviously, the invariants are not maintained in the
 * meantime then and functions relying on them might not work.
 *
 * If nothing is changed, this function does nothing and it should not be slower than checking a
 * flag on every data-block in the given bmain.
 *
 * Sometimes, it is known that only a single or very few data-blocks have been changed (e.g. when a
 * node has been inserted in a node tree). Passing in #modified_ids can speed up the function
 * because it may avoid the need to iterate over all data-blocks to find modified data-blocks.
 *
 * Examples of maintained invariants:
 * - Group nodes need to have the correct sockets based on the referenced node group.
 * - The geometry nodes modifier needs to have the correct inputs based on the referenced group.
 */
void BKE_main_ensure_invariants(Main &bmain,
                                std::optional<blender::Span<ID *>> modified_ids = std::nullopt);
```

This also adds `windowmanager` as a dependency of `blenkernel` to be able to
send notifiers.

Pull Request: https://projects.blender.org/blender/blender/pulls/132023
This commit is contained in:
Jacques Lucke
2025-01-13 15:03:24 +01:00
parent e705073f94
commit 1fae5fd8f6
5 changed files with 116 additions and 38 deletions

View File

@@ -0,0 +1,37 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_span.hh"
#include <optional>
struct Main;
struct ID;
/**
* Makes sure that invariants in original DNA data are maintained after changes.
*
* This function has to be idempotent, i.e. after calling it once, additional calls should not
* modify DNA data further. If it would, it would imply that this function does more than
* maintaining invariants.
*
* This has to be called after any kind of change to original DNA data that may be involved in some
* of the maintained invariants. It's possible to do multiple changes in a row and then fixing all
* invariants with a single call in the end. Obviously, the invariants are not maintained in the
* meantime then and functions relying on them might not work.
*
* If nothing is changed, this function does nothing and it should not be slower than checking a
* flag on every data-block in the given bmain.
*
* Sometimes, it is known that only a single or very few data-blocks have been changed (e.g. when a
* node has been inserted in a node tree). Passing in #modified_ids can speed up the function
* because it may avoid the need to iterate over all data-blocks to find modified data-blocks.
*
* Examples of maintained invariants:
* - Group nodes need to have the correct sockets based on the referenced node group.
* - The geometry nodes modifier needs to have the correct inputs based on the referenced group.
*/
void BKE_main_ensure_invariants(Main &bmain,
std::optional<blender::Span<ID *>> modified_ids = std::nullopt);

View File

@@ -10,6 +10,7 @@ set(INC
../nodes/geometry/include
../shader_fx
../simulation
../windowmanager
../../../intern/eigen
../../../intern/ghost
../../../intern/iksolver/extern
@@ -180,6 +181,7 @@ set(SRC
intern/linestyle.cc
intern/main.cc
intern/main_idmap.cc
intern/main_invariants.cc
intern/main_namemap.cc
intern/mask.cc
intern/mask_evaluate.cc
@@ -432,6 +434,7 @@ set(SRC
BKE_linestyle.h
BKE_main.hh
BKE_main_idmap.hh
BKE_main_invariants.hh
BKE_main_namemap.hh
BKE_mask.h
BKE_material.hh

View File

@@ -0,0 +1,70 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_main_invariants.hh"
#include "BKE_node_tree_update.hh"
#include "DEG_depsgraph.hh"
#include "DNA_node_types.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include <optional>
static void send_notifiers_after_node_tree_change(ID *id, bNodeTree *ntree)
{
WM_main_add_notifier(NC_NODE | NA_EDITED, id);
if (ntree->type == NTREE_SHADER && id != nullptr) {
if (GS(id->name) == ID_MA) {
WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id);
}
else if (GS(id->name) == ID_LA) {
WM_main_add_notifier(NC_LAMP | ND_LIGHTING, id);
}
else if (GS(id->name) == ID_WO) {
WM_main_add_notifier(NC_WORLD | ND_WORLD, id);
}
}
else if (ntree->type == NTREE_COMPOSIT) {
WM_main_add_notifier(NC_SCENE | ND_NODES, id);
}
else if (ntree->type == NTREE_TEXTURE) {
WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
}
else if (ntree->type == NTREE_GEOMETRY) {
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
}
}
static void propagate_node_tree_changes(Main &bmain,
const std::optional<blender::Span<ID *>> modified_ids)
{
NodeTreeUpdateExtraParams params;
params.tree_changed_fn = [](bNodeTree &ntree, ID &owner_id) {
send_notifiers_after_node_tree_change(&owner_id, &ntree);
DEG_id_tag_update(&ntree.id, ID_RECALC_SYNC_TO_EVAL);
};
params.tree_output_changed_fn = [](bNodeTree &ntree, ID & /*owner_id*/) {
DEG_id_tag_update(&ntree.id, ID_RECALC_NTREE_OUTPUT);
};
std::optional<blender::Vector<bNodeTree *>> modified_trees;
if (modified_ids.has_value()) {
modified_trees.emplace();
for (ID *id : *modified_ids) {
if (GS(id->name) == ID_NT) {
modified_trees->append(reinterpret_cast<bNodeTree *>(id));
}
}
}
BKE_ntree_update(bmain, modified_trees, params);
}
void BKE_main_ensure_invariants(Main &bmain, const std::optional<blender::Span<ID *>> modified_ids)
{
propagate_node_tree_changes(bmain, modified_ids);
}

View File

@@ -78,6 +78,7 @@
#include "BKE_lib_remap.hh"
#include "BKE_main.hh" /* for Main */
#include "BKE_main_idmap.hh"
#include "BKE_main_invariants.hh"
#include "BKE_main_namemap.hh"
#include "BKE_material.hh"
#include "BKE_mesh.hh"
@@ -3946,8 +3947,8 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
* Proper fix involves first addressing #90610. */
BKE_main_collections_parent_relations_rebuild(bfd->main);
/* Update node trees after re-generating overrides. */
BKE_ntree_update(*bfd->main);
/* Update invariants after re-generating overrides. */
BKE_main_ensure_invariants(*bfd->main);
fd->reports->duration.lib_overrides = BLI_time_now_seconds() -
fd->reports->duration.lib_overrides;

View File

@@ -22,6 +22,7 @@
#include "BKE_image.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_main_invariants.hh"
#include "BKE_material.hh"
#include "BKE_node.hh"
#include "BKE_node_legacy_types.hh"
@@ -460,32 +461,6 @@ bool composite_node_editable(bContext *C)
return false;
}
static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree)
{
WM_main_add_notifier(NC_NODE | NA_EDITED, id);
if (ntree->type == NTREE_SHADER && id != nullptr) {
if (GS(id->name) == ID_MA) {
WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id);
}
else if (GS(id->name) == ID_LA) {
WM_main_add_notifier(NC_LAMP | ND_LIGHTING, id);
}
else if (GS(id->name) == ID_WO) {
WM_main_add_notifier(NC_WORLD | ND_WORLD, id);
}
}
else if (ntree->type == NTREE_COMPOSIT) {
WM_main_add_notifier(NC_SCENE | ND_NODES, id);
}
else if (ntree->type == NTREE_TEXTURE) {
WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
}
else if (ntree->type == NTREE_GEOMETRY) {
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
}
}
/** \} */
} // namespace blender::ed::space_node
@@ -496,19 +471,11 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree)
void ED_node_tree_propagate_change(Main &bmain, bNodeTree *root_ntree)
{
NodeTreeUpdateExtraParams params;
params.tree_changed_fn = [](bNodeTree &ntree, ID &owner_id) {
blender::ed::space_node::send_notifiers_after_tree_change(&owner_id, &ntree);
DEG_id_tag_update(&ntree.id, ID_RECALC_SYNC_TO_EVAL);
};
params.tree_output_changed_fn = [](bNodeTree &ntree, ID & /*owner_id*/) {
DEG_id_tag_update(&ntree.id, ID_RECALC_NTREE_OUTPUT);
};
if (root_ntree) {
BKE_ntree_update_after_single_tree_change(bmain, *root_ntree, params);
BKE_main_ensure_invariants(bmain, {{&root_ntree->id}});
}
else {
BKE_ntree_update(bmain, std::nullopt, params);
BKE_main_ensure_invariants(bmain);
}
}