The main goal of these changes is to support checking if some data has been changed over time. This is used by the WIP simulation nodes during baking to detect which attributes have to be stored in every frame because they have changed. By using a combination of a weak user count and a version counter, it is possible to detect that an attribute (or any data controlled by implicit sharing) has not been changed with O(1) memory and time. It's still possible that the data has been changed multiple times and is the same in the end and beginning of course. That wouldn't be detected using this mechanism. The `ImplicitSharingInfo` struct has a new weak user count. A weak reference is one that does not keep the referenced data alive, but makes sure that the `ImplicitSharingInfo` itself is not deleted. If some piece of data has one strong and multiple weak users, it is still mutable. If the strong user count goes down to zero, the referenced data is freed. Remaining weak users can check for this condition using `is_expired`. This is a bit similar to `std::weak_ptr` but there is an important difference: a weak user can not become a strong user while one can create a `shared_ptr` from a `weak_ptr`. This restriction is necessary, because some code might be changing the referenced data assuming that it is the only owner. If another thread suddenly adds a new owner, the data would be shared again and the first thread would not have been allowed to modify the data in the first place. There is also a new integer version counter in `ImplicitSharingInfo`. It is incremented whenever some code wants to modify the referenced data. Obviously, this can only be done when the data is not shared because then it would be immutable. By comparing an old and new version number of the same sharing info, one can check if the data has been modified. One has to keep a weak reference to the sharing info together with the old version number to ensure that the new sharing info is still the same as the old one. Without this, it can happen that the sharing info was freed and a new one was allocated at the same pointer address. Using a strong reference for this purpose does not work, because then the data would never be modified because it's shared.
233 lines
7.5 KiB
C++
233 lines
7.5 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "DNA_pointcloud_types.h"
|
|
|
|
#include "BKE_geometry_set.hh"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_pointcloud.h"
|
|
|
|
#include "attribute_access_intern.hh"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Geometry Component Implementation
|
|
* \{ */
|
|
|
|
PointCloudComponent::PointCloudComponent() : GeometryComponent(GEO_COMPONENT_TYPE_POINT_CLOUD) {}
|
|
|
|
PointCloudComponent::~PointCloudComponent()
|
|
{
|
|
this->clear();
|
|
}
|
|
|
|
GeometryComponent *PointCloudComponent::copy() const
|
|
{
|
|
PointCloudComponent *new_component = new PointCloudComponent();
|
|
if (pointcloud_ != nullptr) {
|
|
new_component->pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_);
|
|
new_component->ownership_ = GeometryOwnershipType::Owned;
|
|
}
|
|
return new_component;
|
|
}
|
|
|
|
void PointCloudComponent::clear()
|
|
{
|
|
BLI_assert(this->is_mutable() || this->is_expired());
|
|
if (pointcloud_ != nullptr) {
|
|
if (ownership_ == GeometryOwnershipType::Owned) {
|
|
BKE_id_free(nullptr, pointcloud_);
|
|
}
|
|
pointcloud_ = nullptr;
|
|
}
|
|
}
|
|
|
|
bool PointCloudComponent::has_pointcloud() const
|
|
{
|
|
return pointcloud_ != nullptr;
|
|
}
|
|
|
|
void PointCloudComponent::replace(PointCloud *pointcloud, GeometryOwnershipType ownership)
|
|
{
|
|
BLI_assert(this->is_mutable());
|
|
this->clear();
|
|
pointcloud_ = pointcloud;
|
|
ownership_ = ownership;
|
|
}
|
|
|
|
PointCloud *PointCloudComponent::release()
|
|
{
|
|
BLI_assert(this->is_mutable());
|
|
PointCloud *pointcloud = pointcloud_;
|
|
pointcloud_ = nullptr;
|
|
return pointcloud;
|
|
}
|
|
|
|
const PointCloud *PointCloudComponent::get_for_read() const
|
|
{
|
|
return pointcloud_;
|
|
}
|
|
|
|
PointCloud *PointCloudComponent::get_for_write()
|
|
{
|
|
BLI_assert(this->is_mutable());
|
|
if (ownership_ == GeometryOwnershipType::ReadOnly) {
|
|
pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_);
|
|
ownership_ = GeometryOwnershipType::Owned;
|
|
}
|
|
return pointcloud_;
|
|
}
|
|
|
|
bool PointCloudComponent::is_empty() const
|
|
{
|
|
return pointcloud_ == nullptr;
|
|
}
|
|
|
|
bool PointCloudComponent::owns_direct_data() const
|
|
{
|
|
return ownership_ == GeometryOwnershipType::Owned;
|
|
}
|
|
|
|
void PointCloudComponent::ensure_owns_direct_data()
|
|
{
|
|
BLI_assert(this->is_mutable());
|
|
if (ownership_ != GeometryOwnershipType::Owned) {
|
|
pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_);
|
|
ownership_ = GeometryOwnershipType::Owned;
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Attribute Access
|
|
* \{ */
|
|
|
|
namespace blender::bke {
|
|
|
|
static void tag_component_positions_changed(void *owner)
|
|
{
|
|
PointCloud &points = *static_cast<PointCloud *>(owner);
|
|
points.tag_positions_changed();
|
|
}
|
|
|
|
static void tag_component_radius_changed(void *owner)
|
|
{
|
|
PointCloud &points = *static_cast<PointCloud *>(owner);
|
|
points.tag_radii_changed();
|
|
}
|
|
|
|
/**
|
|
* In this function all the attribute providers for a point cloud component are created. Most data
|
|
* in this function is statically allocated, because it does not change over time.
|
|
*/
|
|
static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
|
{
|
|
static CustomDataAccessInfo point_access = {
|
|
[](void *owner) -> CustomData * {
|
|
PointCloud *pointcloud = static_cast<PointCloud *>(owner);
|
|
return &pointcloud->pdata;
|
|
},
|
|
[](const void *owner) -> const CustomData * {
|
|
const PointCloud *pointcloud = static_cast<const PointCloud *>(owner);
|
|
return &pointcloud->pdata;
|
|
},
|
|
[](const void *owner) -> int {
|
|
const PointCloud *pointcloud = static_cast<const PointCloud *>(owner);
|
|
return pointcloud->totpoint;
|
|
}};
|
|
|
|
static BuiltinCustomDataLayerProvider position("position",
|
|
ATTR_DOMAIN_POINT,
|
|
CD_PROP_FLOAT3,
|
|
CD_PROP_FLOAT3,
|
|
BuiltinAttributeProvider::Creatable,
|
|
BuiltinAttributeProvider::NonDeletable,
|
|
point_access,
|
|
tag_component_positions_changed);
|
|
static BuiltinCustomDataLayerProvider radius("radius",
|
|
ATTR_DOMAIN_POINT,
|
|
CD_PROP_FLOAT,
|
|
CD_PROP_FLOAT,
|
|
BuiltinAttributeProvider::Creatable,
|
|
BuiltinAttributeProvider::Deletable,
|
|
point_access,
|
|
tag_component_radius_changed);
|
|
static BuiltinCustomDataLayerProvider id("id",
|
|
ATTR_DOMAIN_POINT,
|
|
CD_PROP_INT32,
|
|
CD_PROP_INT32,
|
|
BuiltinAttributeProvider::Creatable,
|
|
BuiltinAttributeProvider::Deletable,
|
|
point_access,
|
|
nullptr);
|
|
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
|
|
return ComponentAttributeProviders({&position, &radius, &id}, {&point_custom_data});
|
|
}
|
|
|
|
static AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
|
{
|
|
static const ComponentAttributeProviders providers =
|
|
create_attribute_providers_for_point_cloud();
|
|
AttributeAccessorFunctions fn =
|
|
attribute_accessor_functions::accessor_functions_for_providers<providers>();
|
|
fn.domain_size = [](const void *owner, const eAttrDomain domain) {
|
|
if (owner == nullptr) {
|
|
return 0;
|
|
}
|
|
const PointCloud &pointcloud = *static_cast<const PointCloud *>(owner);
|
|
switch (domain) {
|
|
case ATTR_DOMAIN_POINT:
|
|
return pointcloud.totpoint;
|
|
default:
|
|
return 0;
|
|
}
|
|
};
|
|
fn.domain_supported = [](const void * /*owner*/, const eAttrDomain domain) {
|
|
return domain == ATTR_DOMAIN_POINT;
|
|
};
|
|
fn.adapt_domain = [](const void * /*owner*/,
|
|
const blender::GVArray &varray,
|
|
const eAttrDomain from_domain,
|
|
const eAttrDomain to_domain) {
|
|
if (from_domain == to_domain && from_domain == ATTR_DOMAIN_POINT) {
|
|
return varray;
|
|
}
|
|
return blender::GVArray{};
|
|
};
|
|
return fn;
|
|
}
|
|
|
|
static const AttributeAccessorFunctions &get_pointcloud_accessor_functions_ref()
|
|
{
|
|
static const AttributeAccessorFunctions fn = get_pointcloud_accessor_functions();
|
|
return fn;
|
|
}
|
|
|
|
} // namespace blender::bke
|
|
|
|
blender::bke::AttributeAccessor PointCloud::attributes() const
|
|
{
|
|
return blender::bke::AttributeAccessor(this,
|
|
blender::bke::get_pointcloud_accessor_functions_ref());
|
|
}
|
|
|
|
blender::bke::MutableAttributeAccessor PointCloud::attributes_for_write()
|
|
{
|
|
return blender::bke::MutableAttributeAccessor(
|
|
this, blender::bke::get_pointcloud_accessor_functions_ref());
|
|
}
|
|
|
|
std::optional<blender::bke::AttributeAccessor> PointCloudComponent::attributes() const
|
|
{
|
|
return blender::bke::AttributeAccessor(pointcloud_,
|
|
blender::bke::get_pointcloud_accessor_functions_ref());
|
|
}
|
|
|
|
std::optional<blender::bke::MutableAttributeAccessor> PointCloudComponent::attributes_for_write()
|
|
{
|
|
PointCloud *pointcloud = this->get_for_write();
|
|
return blender::bke::MutableAttributeAccessor(
|
|
pointcloud, blender::bke::get_pointcloud_accessor_functions_ref());
|
|
}
|
|
|
|
/** \} */
|