From 27da305a404f72a75a37892e1ac080c6531d059b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 24 Jun 2021 19:13:52 +1000 Subject: Depsgraph: support flushing parameters without a full COW update Avoid computationally expensive copying operations when only some settings have been modified. This is done by adding support for updating parameters without tagging for copy-on-write. Currently only mesh data blocks are supported, other data-blocks can be added individually. This prepares for changing values such as edit-mesh auto-smooth angle in edit-mode without duplicating all mesh-data. The benefit will only be seen when the user interface no longer tags all ID's for copy on write updates. ID_RECALC_GEOMETRY_ALL_MODES has been added to support situations where non edit-mode geometry is modified in edit-mode. While this isn't something user are likely to do, Python scripts may change the underlying mesh. Reviewed By: sergey Ref D11377 --- source/blender/blenkernel/BKE_lib_id.h | 3 ++ source/blender/blenkernel/CMakeLists.txt | 1 + source/blender/blenkernel/intern/lib_id_eval.c | 48 ++++++++++++++++++++++ source/blender/blenkernel/intern/mesh_validate.c | 4 +- source/blender/blenkernel/intern/paint.c | 2 +- .../depsgraph/intern/builder/deg_builder_nodes.cc | 14 ++++++- source/blender/depsgraph/intern/depsgraph_tag.cc | 3 ++ .../depsgraph/intern/node/deg_node_component.h | 18 +++++++- source/blender/editors/mesh/editmesh_tools.c | 2 +- source/blender/makesdna/DNA_ID.h | 21 ++++++++++ source/blender/makesrna/intern/rna_mesh.c | 19 +++++++-- source/blender/makesrna/intern/rna_mesh_api.c | 2 +- source/blender/python/bmesh/bmesh_py_types.c | 2 +- 13 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 source/blender/blenkernel/intern/lib_id_eval.c (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index e16507bf3cc..fac5dc8c010 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -315,6 +315,9 @@ void BKE_id_blend_write(struct BlendWriter *writer, struct ID *id); #define IS_TAGGED(_id) ((_id) && (((ID *)_id)->tag & LIB_TAG_DOIT)) +/* lib_id_eval.c */ +void BKE_id_eval_properties_copy(struct ID *id_cow, struct ID *id); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 0f36887b234..20663f0a790 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -165,6 +165,7 @@ set(SRC intern/layer_utils.c intern/lib_id.c intern/lib_id_delete.c + intern/lib_id_eval.c intern/lib_override.c intern/lib_query.c intern/lib_remap.c diff --git a/source/blender/blenkernel/intern/lib_id_eval.c b/source/blender/blenkernel/intern/lib_id_eval.c new file mode 100644 index 00000000000..140fe403ac3 --- /dev/null +++ b/source/blender/blenkernel/intern/lib_id_eval.c @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + * + * Contains management of ID's and libraries + * allocate and free of all library data + */ + +#include "DNA_ID.h" +#include "DNA_mesh_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_lib_id.h" +#include "BKE_mesh.h" + +/** + * Copy relatives parameters, from `id` to `id_cow`. + * Use handle the #ID_RECALC_PARAMETERS tag. + * \note Keep in sync with #ID_TYPE_SUPPORTS_PARAMS_WITHOUT_COW. + */ +void BKE_id_eval_properties_copy(ID *id_cow, ID *id) +{ + const ID_Type id_type = GS(id->name); + BLI_assert((id_cow->tag & LIB_TAG_COPIED_ON_WRITE) && !(id->tag & LIB_TAG_COPIED_ON_WRITE)); + BLI_assert(ID_TYPE_SUPPORTS_PARAMS_WITHOUT_COW(id_type)); + if (id_type == ID_ME) { + BKE_mesh_copy_parameters((Mesh *)id_cow, (const Mesh *)id); + } + else { + BLI_assert_unreachable(); + } +} diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index df84cf6607f..bfdbf844a26 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -1105,7 +1105,7 @@ bool BKE_mesh_validate(Mesh *me, const bool do_verbose, const bool cddata_check_ &changed); if (changed) { - DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY_ALL_MODES); return true; } @@ -1183,7 +1183,7 @@ bool BKE_mesh_validate_material_indices(Mesh *me) } if (!is_valid) { - DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY_ALL_MODES); return true; } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 3494630e1fa..a7c6dc2deb9 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1810,7 +1810,7 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object) CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert); BKE_mesh_update_customdata_pointers(orig_me, true); - DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES); } /** \warning Expects a fully evaluated depsgraph. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 20169f0a961..56168739fae 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1215,7 +1215,19 @@ void DepsgraphNodeBuilder::build_parameters(ID *id) op_node = add_operation_node(id, NodeType::PARAMETERS, OperationCode::PARAMETERS_ENTRY); op_node->set_as_entry(); /* Generic evaluation node. */ - add_operation_node(id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL); + + if (ID_TYPE_SUPPORTS_PARAMS_WITHOUT_COW(GS(id->name))) { + ID *id_cow = get_cow_id(id); + add_operation_node( + id, + NodeType::PARAMETERS, + OperationCode::PARAMETERS_EVAL, + [id_cow, id](::Depsgraph * /*depsgraph*/) { BKE_id_eval_properties_copy(id_cow, id); }); + } + else { + add_operation_node(id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL); + } + /* Explicit exit operation. */ op_node = add_operation_node(id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT); op_node->set_as_exit(); diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 21a6728c364..b00cae87311 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -230,6 +230,7 @@ void depsgraph_tag_to_component_opcode(const ID *id, case ID_RECALC_SOURCE: *component_type = NodeType::PARAMETERS; break; + case ID_RECALC_GEOMETRY_ALL_MODES: case ID_RECALC_ALL: case ID_RECALC_PSYS_ALL: BLI_assert(!"Should not happen"); @@ -705,6 +706,8 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "TRANSFORM"; case ID_RECALC_GEOMETRY: return "GEOMETRY"; + case ID_RECALC_GEOMETRY_ALL_MODES: + return "GEOMETRY_ALL_MODES"; case ID_RECALC_ANIMATION: return "ANIMATION"; case ID_RECALC_PSYS_REDO: diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index 06582c88d8b..a7c69b27654 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -23,7 +23,9 @@ #pragma once +#include "intern/eval/deg_eval_copy_on_write.h" #include "intern/node/deg_node.h" +#include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" #include "BLI_string.h" @@ -172,7 +174,6 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(CopyOnWrite); DEG_COMPONENT_NODE_DECLARE_GENERIC(Geometry); DEG_COMPONENT_NODE_DECLARE_GENERIC(ImageAnimation); DEG_COMPONENT_NODE_DECLARE_GENERIC(LayerCollections); -DEG_COMPONENT_NODE_DECLARE_GENERIC(Parameters); DEG_COMPONENT_NODE_DECLARE_GENERIC(Particles); DEG_COMPONENT_NODE_DECLARE_GENERIC(ParticleSettings); DEG_COMPONENT_NODE_DECLARE_GENERIC(Pose); @@ -199,6 +200,21 @@ struct BoneComponentNode : public ComponentNode { DEG_COMPONENT_NODE_DECLARE; }; +/* Eventually we would not tag parameters in all cases. + * Support for this each ID needs to be added on an individual basis. */ +struct ParametersComponentNode : public ComponentNode { + virtual bool need_tag_cow_before_update() override + { + if (ID_TYPE_SUPPORTS_PARAMS_WITHOUT_COW(owner->id_type)) { + BLI_assert(deg_copy_on_write_is_expanded(owner->id_cow)); + return false; + } + return true; + } + + DEG_COMPONENT_NODE_DECLARE; +}; + void deg_register_component_depsnodes(); } // namespace deg diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index ea4bdc551a2..ee520753478 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4753,7 +4753,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) .calc_object_remap = true, })); - DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY_ALL_MODES); WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 16f5f0b4ae8..15a4f8817fd 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -454,6 +454,10 @@ typedef struct PreviewImage { #define ID_TYPE_IS_COW(_id_type) \ (!ELEM(_id_type, ID_LI, ID_IP, ID_SCR, ID_VF, ID_BR, ID_WM, ID_PAL, ID_PC, ID_WS, ID_IM)) +/* Check whether data-block type requires copy-on-write from #ID_RECALC_PARAMETERS. + * Keep in sync with #BKE_id_eval_properties_copy. */ +#define ID_TYPE_SUPPORTS_PARAMS_WITHOUT_COW(id_type) ELEM(id_type, ID_ME) + #ifdef GS # undef GS #endif @@ -602,10 +606,18 @@ typedef enum IDRecalcFlag { * * When object of armature type gets tagged with this flag, its pose is * re-evaluated. + * * When object of other type is tagged with this flag it makes the modifier * stack to be re-evaluated. + * * When object data type (mesh, curve, ...) gets tagged with this flag it * makes all objects which shares this data-block to be updated. + * + * Note that the evaluation depends on the object-mode. + * So edit-mesh data for example only reevaluate with the updated edit-mesh. + * When geometry in the original ID has been modified #ID_RECALC_GEOMETRY_ALL_MODES + * must be used instead. + * * When a collection gets tagged with this flag, all objects depending on the geometry and * transforms on any of the objects in the collection are updated. */ ID_RECALC_GEOMETRY = (1 << 1), @@ -665,6 +677,10 @@ typedef enum IDRecalcFlag { ID_RECALC_AUDIO = (1 << 20), + /* NOTE: This triggers copy on write for types that require it. + * Exceptions to this can be added using #ID_TYPE_SUPPORTS_PARAMS_WITHOUT_COW, + * this has the advantage that large arrays stored in the idea data don't + * have to be copied on every update. */ ID_RECALC_PARAMETERS = (1 << 21), /* Input has changed and datablock is to be reload from disk. @@ -689,6 +705,11 @@ typedef enum IDRecalcFlag { * all dependent objects. */ ID_RECALC_ANIMATION_NO_FLUSH = ID_RECALC_COPY_ON_WRITE, + /* Ensure geometry of object and edit modes are both up-to-date in the evaluated data-block. + * Example usage is when mesh validation modifies the non-edit-mode data, + * which we want to be copied over to the evaluated data-block. */ + ID_RECALC_GEOMETRY_ALL_MODES = ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE, + /*************************************************************************** * Aggregate flags, use only for checks on runtime. * Do NOT use those for tagging. */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 77eed95d5be..2229fca1b45 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -239,6 +239,19 @@ static void rna_Mesh_update_data_legacy_deg_tag_all(Main *UNUSED(bmain), WM_main_add_notifier(NC_GEOM | ND_DATA, id); } +static void rna_Mesh_update_geom_and_params(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + ID *id = ptr->owner_id; + if (id->us <= 0) { /* See note in section heading. */ + return; + } + + DEG_id_tag_update(id, ID_RECALC_GEOMETRY | ID_RECALC_PARAMETERS); + WM_main_add_notifier(NC_GEOM | ND_DATA, id); +} + static void rna_Mesh_update_data_edit_weight(Main *bmain, Scene *scene, PointerRNA *ptr) { BKE_mesh_batch_cache_dirty_tag(rna_mesh(ptr), BKE_MESH_BATCH_DIRTY_ALL); @@ -3333,7 +3346,7 @@ static void rna_def_mesh(BlenderRNA *brna) "Auto Smooth", "Auto smooth (based on smooth/sharp faces/edges and angle between faces), " "or use custom split normals data if available"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_geom_and_params"); prop = RNA_def_property(srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "smoothresh"); @@ -3342,7 +3355,7 @@ static void rna_def_mesh(BlenderRNA *brna) "Auto Smooth Angle", "Maximum angle between face normals that will be considered as smooth " "(unused if custom split normals data are available)"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_geom_and_params"); RNA_define_verify_sdna(false); prop = RNA_def_property(srna, "has_custom_normals", PROP_BOOLEAN, PROP_NONE); @@ -3373,7 +3386,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop, "Auto Texture Space", "Adjust active object's texture space automatically when transforming object"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_geom_and_params"); # if 0 prop = RNA_def_property(srna, "texspace_location", PROP_FLOAT, PROP_TRANSLATION); diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 2b0582cae9a..8128bdb83a0 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -208,7 +208,7 @@ static void rna_Mesh_clear_geometry(Mesh *mesh) { BKE_mesh_clear_geometry(mesh); - DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY_ALL_MODES); WM_main_add_notifier(NC_GEOM | ND_DATA, mesh); } diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 323661e7ca9..2001392f474 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1060,7 +1060,7 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args) /* we could have the user do this but if they forget blender can easy crash * since the references arrays for the objects derived meshes are now invalid */ - DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY_ALL_MODES); Py_RETURN_NONE; } -- cgit v1.2.3