Fix: Removing attributes from UI invalidates caches

Use the new attribute API to implement the attribute remove function
used by RNA, except for BMesh attributes. Currently, removing curve
attributes from the panel in the property editor does not mark the
relevant caches dirty (for example, the cache of curve type counts),
because that behavior is implemented with the new attribute API.
Also, eventually we want to merge the two APIs, and removing an
attribute is the first function that can be partially implemented
with the new API.

Differential Revision: https://developer.blender.org/D15495
This commit is contained in:
Hans Goudey 2022-07-23 19:59:59 -05:00
parent 0c3851d31f
commit c94c0d988a
3 changed files with 51 additions and 13 deletions

View File

@ -8,6 +8,7 @@
*/
#include <cstring>
#include <optional>
#include "MEM_guardedalloc.h"
@ -24,7 +25,7 @@
#include "BKE_attribute.h"
#include "BKE_attribute.hh"
#include "BKE_curves.h"
#include "BKE_curves.hh"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_pointcloud.h"
@ -89,6 +90,36 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
}
}
namespace blender::bke {
static std::optional<blender::bke::MutableAttributeAccessor> get_attribute_accessor_for_write(
ID &id)
{
switch (GS(id.name)) {
case ID_ME: {
Mesh &mesh = reinterpret_cast<Mesh &>(id);
/* The attribute API isn't implemented for BMesh, so edit mode meshes are not supported. */
BLI_assert(mesh.edit_mesh == nullptr);
return mesh_attributes_for_write(mesh);
}
case ID_PT: {
PointCloud &pointcloud = reinterpret_cast<PointCloud &>(id);
return pointcloud_attributes_for_write(pointcloud);
}
case ID_CV: {
Curves &curves_id = reinterpret_cast<Curves &>(id);
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
return curves.attributes_for_write();
}
default: {
BLI_assert_unreachable();
return {};
}
}
}
} // namespace blender::bke
bool BKE_id_attributes_supported(const ID *id)
{
DomainInfo info[ATTR_DOMAIN_NUM];
@ -242,6 +273,7 @@ CustomDataLayer *BKE_id_attribute_duplicate(ID *id, const char *name, ReportList
bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports)
{
using namespace blender::bke;
if (BKE_id_attribute_required(id, name)) {
BKE_report(reports, RPT_ERROR, "Attribute is required and can't be removed");
return false;
@ -266,12 +298,9 @@ bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports)
ATTR_FALLTHROUGH;
}
default:
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
if (CustomData *data = info[domain].customdata) {
if (CustomData_free_layer_named(data, name, info[domain].length)) {
return true;
}
}
if (std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(
*id)) {
return attributes->remove(name);
}
return false;
}

View File

@ -319,8 +319,8 @@ GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner)
}
std::function<void()> tag_modified_fn;
if (update_on_write_ != nullptr) {
tag_modified_fn = [owner, update = update_on_write_]() { update(owner); };
if (update_on_change_ != nullptr) {
tag_modified_fn = [owner, update = update_on_change_]() { update(owner); };
}
return {as_write_attribute_(data, element_num), domain_, std::move(tag_modified_fn)};
@ -336,12 +336,19 @@ bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const
return {};
}
auto update = [&]() {
if (update_on_change_ != nullptr) {
update_on_change_(owner);
}
};
const int element_num = custom_data_access_.get_element_num(owner);
if (stored_as_named_attribute_) {
if (CustomData_free_layer_named(custom_data, name_.c_str(), element_num)) {
if (custom_data_access_.update_custom_data_pointers) {
custom_data_access_.update_custom_data_pointers(owner);
}
update();
return true;
}
return false;
@ -352,8 +359,10 @@ bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const
if (custom_data_access_.update_custom_data_pointers) {
custom_data_access_.update_custom_data_pointers(owner);
}
update();
return true;
}
return false;
}

View File

@ -226,12 +226,12 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
using AsReadAttribute = GVArray (*)(const void *data, int element_num);
using AsWriteAttribute = GVMutableArray (*)(void *data, int element_num);
using UpdateOnRead = void (*)(const void *owner);
using UpdateOnWrite = void (*)(void *owner);
using UpdateOnChange = void (*)(void *owner);
const eCustomDataType stored_type_;
const CustomDataAccessInfo custom_data_access_;
const AsReadAttribute as_read_attribute_;
const AsWriteAttribute as_write_attribute_;
const UpdateOnWrite update_on_write_;
const UpdateOnChange update_on_change_;
bool stored_as_named_attribute_;
public:
@ -245,14 +245,14 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
const CustomDataAccessInfo custom_data_access,
const AsReadAttribute as_read_attribute,
const AsWriteAttribute as_write_attribute,
const UpdateOnWrite update_on_write)
const UpdateOnChange update_on_write)
: BuiltinAttributeProvider(
std::move(attribute_name), domain, attribute_type, creatable, writable, deletable),
stored_type_(stored_type),
custom_data_access_(custom_data_access),
as_read_attribute_(as_read_attribute),
as_write_attribute_(as_write_attribute),
update_on_write_(update_on_write),
update_on_change_(update_on_write),
stored_as_named_attribute_(data_type_ == stored_type_)
{
}