/* SPDX-FileCopyrightText: 2025 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "testing/testing.h" #include "BKE_attribute.hh" #include "BKE_attribute_storage.hh" namespace blender::bke::tests { TEST(attribute_storage, Empty) { AttributeStorage storage; int count = 0; storage.foreach([&](const Attribute & /*attribute*/) { count++; }); EXPECT_EQ(count, 0); } TEST(attribute_storage, Single) { AttributeStorage storage; auto *sharing_info = new ImplicitSharedValue>(Span{1.5f, 1.2f, 1.1f, 1.0f}); Attribute::ArrayData data{}; data.sharing_info = ImplicitSharingPtr<>(sharing_info); data.data = sharing_info->data.data(); data.size = 4; storage.add("foo", AttrDomain::Corner, AttrType::Float, std::move(data)); EXPECT_TRUE(storage.lookup("foo")); EXPECT_EQ(storage.lookup("foo")->domain(), AttrDomain::Corner); EXPECT_EQ(storage.lookup("foo")->data_type(), AttrType::Float); { const auto &data = std::get(storage.lookup("foo")->data()); EXPECT_EQ(data.data, sharing_info->data.data()); } int count = 0; storage.foreach([&](const Attribute & /*attribute*/) { count++; }); EXPECT_EQ(count, 1); } TEST(attribute_storage, GetForWrite) { AttributeStorage storage; auto *sharing_info = new ImplicitSharedValue>(Span{1.5f, 1.2f, 1.1f, 1.0f}); Attribute::ArrayData data{}; data.sharing_info = ImplicitSharingPtr<>(sharing_info); data.data = sharing_info->data.data(); data.size = 4; storage.add("foo", AttrDomain::Corner, AttrType::Float, std::move(data)); { const auto &data = std::get(storage.lookup("foo")->data_for_write()); EXPECT_EQ(data.data, sharing_info->data.data()); } { sharing_info->add_user(); const auto &data = std::get(storage.lookup("foo")->data_for_write()); EXPECT_NE(data.data, sharing_info->data.data()); const float *data_ptr = static_cast(data.data); EXPECT_EQ(data_ptr[0], 1.5f); EXPECT_EQ(data_ptr[1], 1.2f); EXPECT_EQ(data_ptr[2], 1.1f); EXPECT_EQ(data_ptr[3], 1.0f); sharing_info->remove_user_and_delete_if_last(); } { const auto &data = std::get(storage.lookup("foo")->data_for_write()); const float *data_ptr = static_cast(data.data); EXPECT_EQ(data_ptr[0], 1.5f); EXPECT_EQ(data_ptr[1], 1.2f); EXPECT_EQ(data_ptr[2], 1.1f); EXPECT_EQ(data_ptr[3], 1.0f); } } TEST(attribute_storage, MultipleShared) { AttributeStorage storage; auto *sharing_info = new ImplicitSharedValue>(Span{1.5f, 1.2f, 1.1f, 1.0f}); Attribute::ArrayData data{}; data.sharing_info = ImplicitSharingPtr<>(sharing_info); data.data = sharing_info->data.data(); data.size = 4; storage.add("we", AttrDomain::Corner, AttrType::Float, data); storage.add("need", AttrDomain::Point, AttrType::Float, data); storage.add("more", AttrDomain::Face, AttrType::Float, data); storage.add("data", AttrDomain::Edge, AttrType::Float, data); /* The same data is shared among 4 attributes (as well as the original `data`). */ EXPECT_EQ(sharing_info->strong_users(), 5); storage.add("final!", AttrDomain::Edge, AttrType::Float, std::move(data)); EXPECT_EQ(sharing_info->strong_users(), 5); { const auto &data = std::get(storage.lookup("more")->data_for_write()); const float *data_ptr = static_cast(data.data); EXPECT_EQ(data_ptr[0], 1.5f); EXPECT_EQ(data_ptr[1], 1.2f); EXPECT_EQ(data_ptr[2], 1.1f); EXPECT_EQ(data_ptr[3], 1.0f); } int count = 0; storage.foreach([&](const Attribute & /*attribute*/) { count++; }); EXPECT_EQ(count, 5); } TEST(attribute_storage, CopyConstruct) { AttributeStorage storage; auto *sharing_info = new ImplicitSharedValue>(Span{1.5f, 1.2f, 1.1f, 1.0f}); Attribute::ArrayData data{}; data.sharing_info = ImplicitSharingPtr<>(sharing_info); data.data = sharing_info->data.data(); data.size = 4; storage.add("foo", AttrDomain::Corner, AttrType::Float, std::move(data)); AttributeStorage copy{storage}; EXPECT_TRUE(copy.lookup("foo")); EXPECT_EQ(copy.lookup("foo")->domain(), AttrDomain::Corner); EXPECT_EQ(copy.lookup("foo")->data_type(), AttrType::Float); { const auto &data = std::get(copy.lookup("foo")->data()); /* The data is shared, so it should be the same as the original. */ EXPECT_EQ(data.data, sharing_info->data.data()); } } TEST(attribute_storage, MoveConstruct) { AttributeStorage storage; auto *sharing_info = new ImplicitSharedValue>(Span{1.5f, 1.2f, 1.1f, 1.0f}); Attribute::ArrayData data{}; data.sharing_info = ImplicitSharingPtr<>(sharing_info); data.data = sharing_info->data.data(); data.size = 4; storage.add("foo", AttrDomain::Corner, AttrType::Float, std::move(data)); AttributeStorage copy{std::move(storage)}; EXPECT_TRUE(copy.lookup("foo")); EXPECT_EQ(copy.lookup("foo")->domain(), AttrDomain::Corner); EXPECT_EQ(copy.lookup("foo")->data_type(), AttrType::Float); { const auto &data = std::get(copy.lookup("foo")->data()); /* The data is shared, so it should be the same as the original. */ EXPECT_EQ(data.data, sharing_info->data.data()); } } TEST(attribute_storage, UniqueNames) { AttributeStorage storage; auto create_array_data = []() { auto *sharing_info = new ImplicitSharedValue>( Span{1.5f, 1.2f, 1.1f, 1.0f}); Attribute::ArrayData data{}; data.sharing_info = ImplicitSharingPtr<>(sharing_info); data.data = sharing_info->data.data(); data.size = 4; return data; }; storage.add("foo", AttrDomain::Corner, AttrType::Float, create_array_data()); storage.add("foo_2", AttrDomain::Face, AttrType::Float, create_array_data()); storage.add("foo_3", AttrDomain::Point, AttrType::Float, create_array_data()); storage.add( storage.unique_name_calc("foo"), AttrDomain::Edge, AttrType::Float, create_array_data()); storage.add( storage.unique_name_calc("foo"), AttrDomain::Corner, AttrType::Float, create_array_data()); storage.add( storage.unique_name_calc("foo_2"), AttrDomain::Point, AttrType::Float, create_array_data()); int count = 0; storage.foreach([&](const Attribute & /*attribute*/) { count++; }); EXPECT_EQ(count, 6); } } // namespace blender::bke::tests