diff options
author | Jacques Lucke <jacques@blender.org> | 2022-07-21 13:47:44 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-07-21 13:47:44 +0300 |
commit | 2034e8c42dd9c09952196fb12a82adde696ac05d (patch) | |
tree | 113602b674448c21c92c7687e9695979ecc75913 /source/blender/blenkernel | |
parent | 538da79c6d17a6e660a9484c280220240b306282 (diff) |
Geometry Nodes: add debug check for whether AttributeWriter.finish is called
Calling `finish` after writing to generic attributes is currently necessary for
correctness. Previously, this was easy to forget. Now there is a check for this
in debug builds.
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_attribute.hh | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/attribute_access.cc | 43 |
2 files changed, 44 insertions, 4 deletions
diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index 835fd58026f..1e61e477759 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -537,10 +537,7 @@ class MutableAttributeAccessor : public AttributeAccessor { * Get a writable attribute or none if it does not exist. * Make sure to call #finish after changes are done. */ - GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id) - { - return fn_->lookup_for_write(owner_, attribute_id); - } + GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id); /** * Get a writable attribute or non if it does not exist. diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index b2ac15a71d7..19faddc5727 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -968,6 +968,49 @@ void MutableAttributeAccessor::remove_anonymous() } } +/** + * Debug utility that checks whether the #finish function of an #AttributeWriter has been called. + */ +#ifdef DEBUG +struct FinishCallChecker { + std::string name; + bool finish_called = false; + std::function<void()> real_finish_fn; + + ~FinishCallChecker() + { + if (!this->finish_called) { + std::cerr << "Forgot to call `finish()` for '" << this->name << "'.\n"; + } + } +}; +#endif + +GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef &attribute_id) +{ + GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id); + /* Check that the #finish method is called in debug builds. */ +#ifdef DEBUG + if (attribute) { + auto checker = std::make_shared<FinishCallChecker>(); + if (attribute_id.is_named()) { + checker->name = attribute_id.name(); + } + else { + checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id()); + } + checker->real_finish_fn = attribute.tag_modified_fn; + attribute.tag_modified_fn = [checker]() { + if (checker->real_finish_fn) { + checker->real_finish_fn(); + } + checker->finish_called = true; + }; + } +#endif + return attribute; +} + GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write( const AttributeIDRef &attribute_id, const eAttrDomain domain, |