Files
test2/source/blender/blenkernel/intern/geometry_component_pointcloud.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

140 lines
3.3 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_pointcloud_types.h"
#include "BKE_geometry_set.hh"
2024-01-15 12:44:04 -05:00
#include "BKE_lib_id.hh"
#include "BKE_pointcloud.hh"
#include "attribute_access_intern.hh"
namespace blender::bke {
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
PointCloudComponent::PointCloudComponent() : GeometryComponent(Type::PointCloud) {}
PointCloudComponent::PointCloudComponent(PointCloud *pointcloud, GeometryOwnershipType ownership)
: GeometryComponent(Type::PointCloud), pointcloud_(pointcloud), ownership_(ownership)
{
}
PointCloudComponent::~PointCloudComponent()
{
this->clear();
}
GeometryComponentPtr 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 GeometryComponentPtr(new_component);
}
void PointCloudComponent::clear()
{
BLI: support weak users and version in implicit sharing info 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.
2023-04-28 12:03:42 +02:00
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() 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) {
if (pointcloud_) {
pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_);
}
ownership_ = GeometryOwnershipType::Owned;
}
}
Core: introduce MemoryCounter API We often have the situation where it would be good if we could easily estimate the memory usage of some value (e.g. a mesh, or volume). Examples of where we ran into this in the past: * Undo step size. * Caching of volume grids. * Caching of loaded geometries for import geometry nodes. Generally, most caching systems would benefit from the ability to know how much memory they currently use to make better decisions about which data to free and when. The goal of this patch is to introduce a simple general API to count the memory usage that is independent of any specific caching system. I'm doing this to "fix" the chicken and egg problem that caches need to know the memory usage, but we don't really need to count the memory usage without using it for caches. Implementing caching and memory counting at the same time make both harder than implementing them one after another. The main difficulty with counting memory usage is that some memory may be shared using implicit sharing. We want to avoid double counting such memory. How exactly shared memory is treated depends a bit on the use case, so no specific assumptions are made about that in the API. The gathered memory usage is not expected to be exact. It's expected to be a decent approximation. It's neither a lower nor an upper bound unless specified by some specific type. Cache systems generally build on top of heuristics to decide when to free what anyway. There are two sides to this API: 1. Get the amount of memory used by one or more values. This side is used by caching systems and/or systems that want to present the used memory to the user. 2. Tell the caller how much memory is used. This side is used by all kinds of types that can report their memory usage such as meshes. ```cpp /* Get how much memory is used by two meshes together. */ MemoryCounter memory; mesh_a->count_memory(memory); mesh_b->count_memory(memory); int64_t bytes_used = memory.counted_bytes(); /* Tell the caller how much memory is used. */ void Mesh::count_memory(blender::MemoryCounter &memory) const { memory.add_shared(this->runtime->face_offsets_sharing_info, this->face_offsets().size_in_bytes()); /* Forward memory counting to lower level types. This should be fairly common. */ CustomData_count_memory(this->vert_data, this->verts_num, memory); } void CustomData_count_memory(const CustomData &data, const int totelem, blender::MemoryCounter &memory) { for (const CustomDataLayer &layer : Span{data.layers, data.totlayer}) { memory.add_shared(layer.sharing_info, [&](blender::MemoryCounter &shared_memory) { /* Not quite correct for all types, but this is only a rough approximation anyway. */ const int64_t elem_size = CustomData_get_elem_size(&layer); shared_memory.add(totelem * elem_size); }); } } ``` Pull Request: https://projects.blender.org/blender/blender/pulls/126295
2024-08-15 10:54:21 +02:00
void PointCloudComponent::count_memory(MemoryCounter &memory) const
{
if (pointcloud_) {
pointcloud_->count_memory(memory);
}
}
/** \} */
} // namespace blender::bke
namespace blender::bke {
/* -------------------------------------------------------------------- */
/** \name Attribute Access
* \{ */
std::optional<AttributeAccessor> PointCloudComponent::attributes() const
Geometry Nodes: new geometry attribute API Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is accessible from RNA and C code. The second is implemented with `GeometryComponent` and is only accessible in C++ code. The second is widely used, but only being accessible through the `GeometrySet` API makes it awkward to use, and even impossible for types that don't correspond directly to a geometry component like `CurvesGeometry`. This patch adds a new attribute API, designed to replace the `GeometryComponent` attribute API now, and to eventually replace or be the basis of the other one. The basic idea is that there is an `AttributeAccessor` class that allows code to interact with a set of attributes owned by some geometry. The accessor itself has no ownership. `AttributeAccessor` is a simple type that can be passed around by value. That makes it easy to return it from functions and to store it in containers. For const-correctness, there is also a `MutableAttributeAccessor` that allows changing individual and can add or remove attributes. Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer to the owner of the attribute data. The second is a pointer to a struct with function pointers, that is similar to a virtual function table. The functions know how to access attributes on the owner. The actual attribute access for geometries is still implemented with the `AttributeProvider` pattern, which makes it easy to support different sources of attributes on a geometry and simplifies dealing with built-in attributes. There are different ways to get an attribute accessor for a geometry: * `GeometryComponent.attributes()` * `CurvesGeometry.attributes()` * `bke::mesh_attributes(const Mesh &)` * `bke::pointcloud_attributes(const PointCloud &)` All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`. Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
{
return AttributeAccessor(pointcloud_, pointcloud_attribute_accessor_functions());
Geometry Nodes: new geometry attribute API Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is accessible from RNA and C code. The second is implemented with `GeometryComponent` and is only accessible in C++ code. The second is widely used, but only being accessible through the `GeometrySet` API makes it awkward to use, and even impossible for types that don't correspond directly to a geometry component like `CurvesGeometry`. This patch adds a new attribute API, designed to replace the `GeometryComponent` attribute API now, and to eventually replace or be the basis of the other one. The basic idea is that there is an `AttributeAccessor` class that allows code to interact with a set of attributes owned by some geometry. The accessor itself has no ownership. `AttributeAccessor` is a simple type that can be passed around by value. That makes it easy to return it from functions and to store it in containers. For const-correctness, there is also a `MutableAttributeAccessor` that allows changing individual and can add or remove attributes. Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer to the owner of the attribute data. The second is a pointer to a struct with function pointers, that is similar to a virtual function table. The functions know how to access attributes on the owner. The actual attribute access for geometries is still implemented with the `AttributeProvider` pattern, which makes it easy to support different sources of attributes on a geometry and simplifies dealing with built-in attributes. There are different ways to get an attribute accessor for a geometry: * `GeometryComponent.attributes()` * `CurvesGeometry.attributes()` * `bke::mesh_attributes(const Mesh &)` * `bke::pointcloud_attributes(const PointCloud &)` All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`. Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
}
std::optional<MutableAttributeAccessor> PointCloudComponent::attributes_for_write()
{
PointCloud *pointcloud = this->get_for_write();
return MutableAttributeAccessor(pointcloud, pointcloud_attribute_accessor_functions());
}
/** \} */
} // namespace blender::bke