1219 lines
43 KiB
C++
1219 lines
43 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "BKE_curves.hh"
|
|
#include "BKE_instances.hh"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_mesh.hh"
|
|
#include "BKE_pointcloud.h"
|
|
#include "BKE_simulation_state_serialize.hh"
|
|
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "BLI_endian_defines.h"
|
|
#include "BLI_endian_switch.h"
|
|
#include "BLI_fileops.hh"
|
|
#include "BLI_math_matrix_types.hh"
|
|
#include "BLI_path_util.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_enum_types.h"
|
|
|
|
namespace blender::bke::sim {
|
|
|
|
/**
|
|
* Turn the name into something that can be used as file name. It does not necessarily have to be
|
|
* human readable, but it can help if it is at least partially readable.
|
|
*/
|
|
static std::string escape_name(const StringRef name)
|
|
{
|
|
std::stringstream ss;
|
|
for (const char c : name) {
|
|
/* Only some letters allowed. Digits are not because they could lead to name collisions. */
|
|
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) {
|
|
ss << c;
|
|
}
|
|
else {
|
|
ss << int(c);
|
|
}
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
static std::string get_blendcache_directory(const Main &bmain)
|
|
{
|
|
StringRefNull blend_file_path = BKE_main_blendfile_path(&bmain);
|
|
char blend_directory[FILE_MAX];
|
|
char blend_name[FILE_MAX];
|
|
BLI_path_split_dir_file(blend_file_path.c_str(),
|
|
blend_directory,
|
|
sizeof(blend_directory),
|
|
blend_name,
|
|
sizeof(blend_name));
|
|
const int64_t type_start_index = StringRef(blend_name).rfind(".");
|
|
if (type_start_index == StringRef::not_found) {
|
|
return "";
|
|
}
|
|
blend_name[type_start_index] = '\0';
|
|
const std::string blendcache_name = "blendcache_" + StringRef(blend_name);
|
|
|
|
char blendcache_dir[FILE_MAX];
|
|
BLI_path_join(blendcache_dir, sizeof(blendcache_dir), blend_directory, blendcache_name.c_str());
|
|
return blendcache_dir;
|
|
}
|
|
|
|
static std::string get_modifier_sim_name(const Object &object, const ModifierData &md)
|
|
{
|
|
const std::string object_name_escaped = escape_name(object.id.name + 2);
|
|
const std::string modifier_name_escaped = escape_name(md.name);
|
|
return "sim_" + object_name_escaped + "_" + modifier_name_escaped;
|
|
}
|
|
|
|
std::string get_bake_directory(const Main &bmain, const Object &object, const ModifierData &md)
|
|
{
|
|
char bdata_dir[FILE_MAX];
|
|
BLI_path_join(bdata_dir,
|
|
sizeof(bdata_dir),
|
|
get_blendcache_directory(bmain).c_str(),
|
|
get_modifier_sim_name(object, md).c_str());
|
|
return bdata_dir;
|
|
}
|
|
|
|
std::string get_bdata_directory(const Main &bmain, const Object &object, const ModifierData &md)
|
|
{
|
|
char bdata_dir[FILE_MAX];
|
|
BLI_path_join(
|
|
bdata_dir, sizeof(bdata_dir), get_bake_directory(bmain, object, md).c_str(), "bdata");
|
|
return bdata_dir;
|
|
}
|
|
|
|
std::string get_meta_directory(const Main &bmain, const Object &object, const ModifierData &md)
|
|
{
|
|
char meta_dir[FILE_MAX];
|
|
BLI_path_join(meta_dir, sizeof(meta_dir), get_bake_directory(bmain, object, md).c_str(), "meta");
|
|
return meta_dir;
|
|
}
|
|
|
|
std::shared_ptr<DictionaryValue> BDataSlice::serialize() const
|
|
{
|
|
auto io_slice = std::make_shared<DictionaryValue>();
|
|
io_slice->append_str("name", this->name);
|
|
io_slice->append_int("start", range.start());
|
|
io_slice->append_int("size", range.size());
|
|
return io_slice;
|
|
}
|
|
|
|
std::optional<BDataSlice> BDataSlice::deserialize(const DictionaryValue &io_slice)
|
|
{
|
|
const std::optional<StringRefNull> name = io_slice.lookup_str("name");
|
|
const std::optional<int64_t> start = io_slice.lookup_int("start");
|
|
const std::optional<int64_t> size = io_slice.lookup_int("size");
|
|
if (!name || !start || !size) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return BDataSlice{*name, {*start, *size}};
|
|
}
|
|
|
|
static StringRefNull get_endian_io_name(const int endian)
|
|
{
|
|
if (endian == L_ENDIAN) {
|
|
return "little";
|
|
}
|
|
BLI_assert(endian == B_ENDIAN);
|
|
return "big";
|
|
}
|
|
|
|
static StringRefNull get_domain_io_name(const eAttrDomain domain)
|
|
{
|
|
const char *io_name = "unknown";
|
|
RNA_enum_id_from_value(rna_enum_attribute_domain_items, domain, &io_name);
|
|
return io_name;
|
|
}
|
|
|
|
static StringRefNull get_data_type_io_name(const eCustomDataType data_type)
|
|
{
|
|
const char *io_name = "unknown";
|
|
RNA_enum_id_from_value(rna_enum_attribute_type_items, data_type, &io_name);
|
|
return io_name;
|
|
}
|
|
|
|
static std::optional<eAttrDomain> get_domain_from_io_name(const StringRefNull io_name)
|
|
{
|
|
int domain;
|
|
if (!RNA_enum_value_from_identifier(rna_enum_attribute_domain_items, io_name.c_str(), &domain)) {
|
|
return std::nullopt;
|
|
}
|
|
return eAttrDomain(domain);
|
|
}
|
|
|
|
static std::optional<eCustomDataType> get_data_type_from_io_name(const StringRefNull io_name)
|
|
{
|
|
int domain;
|
|
if (!RNA_enum_value_from_identifier(rna_enum_attribute_type_items, io_name.c_str(), &domain)) {
|
|
return std::nullopt;
|
|
}
|
|
return eCustomDataType(domain);
|
|
}
|
|
|
|
/**
|
|
* Write the data and remember which endianness the data had.
|
|
*/
|
|
static std::shared_ptr<DictionaryValue> write_bdata_raw_data_with_endian(
|
|
BDataWriter &bdata_writer, const void *data, const int64_t size_in_bytes)
|
|
{
|
|
auto io_data = bdata_writer.write(data, size_in_bytes).serialize();
|
|
if (ENDIAN_ORDER == B_ENDIAN) {
|
|
io_data->append_str("endian", get_endian_io_name(ENDIAN_ORDER));
|
|
}
|
|
return io_data;
|
|
}
|
|
|
|
/**
|
|
* Read data of an into an array and optionally perform an endian switch if necessary.
|
|
*/
|
|
[[nodiscard]] static bool read_bdata_raw_data_with_endian(const BDataReader &bdata_reader,
|
|
const DictionaryValue &io_data,
|
|
const int64_t element_size,
|
|
const int64_t elements_num,
|
|
void *r_data)
|
|
{
|
|
const std::optional<BDataSlice> slice = BDataSlice::deserialize(io_data);
|
|
if (!slice) {
|
|
return false;
|
|
}
|
|
if (slice->range.size() != element_size * elements_num) {
|
|
return false;
|
|
}
|
|
if (!bdata_reader.read(*slice, r_data)) {
|
|
return false;
|
|
}
|
|
const StringRefNull stored_endian = io_data.lookup_str("endian").value_or("little");
|
|
const StringRefNull current_endian = get_endian_io_name(ENDIAN_ORDER);
|
|
const bool need_endian_switch = stored_endian != current_endian;
|
|
if (need_endian_switch) {
|
|
switch (element_size) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
BLI_endian_switch_uint16_array(static_cast<uint16_t *>(r_data), elements_num);
|
|
break;
|
|
case 4:
|
|
BLI_endian_switch_uint32_array(static_cast<uint32_t *>(r_data), elements_num);
|
|
break;
|
|
case 8:
|
|
BLI_endian_switch_uint64_array(static_cast<uint64_t *>(r_data), elements_num);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** Write bytes ignoring endianness. */
|
|
static std::shared_ptr<DictionaryValue> write_bdata_raw_bytes(BDataWriter &bdata_writer,
|
|
const void *data,
|
|
const int64_t size_in_bytes)
|
|
{
|
|
return bdata_writer.write(data, size_in_bytes).serialize();
|
|
}
|
|
|
|
/** Read bytes ignoring endianness. */
|
|
[[nodiscard]] static bool read_bdata_raw_bytes(const BDataReader &bdata_reader,
|
|
const DictionaryValue &io_data,
|
|
const int64_t bytes_num,
|
|
void *r_data)
|
|
{
|
|
const std::optional<BDataSlice> slice = BDataSlice::deserialize(io_data);
|
|
if (!slice) {
|
|
return false;
|
|
}
|
|
if (slice->range.size() != bytes_num) {
|
|
return false;
|
|
}
|
|
return bdata_reader.read(*slice, r_data);
|
|
}
|
|
|
|
static std::shared_ptr<DictionaryValue> write_bdata_simple_gspan(BDataWriter &bdata_writer,
|
|
const GSpan data)
|
|
{
|
|
const CPPType &type = data.type();
|
|
BLI_assert(type.is_trivial());
|
|
if (type.size() == 1 || type.is<ColorGeometry4b>()) {
|
|
return write_bdata_raw_bytes(bdata_writer, data.data(), data.size_in_bytes());
|
|
}
|
|
return write_bdata_raw_data_with_endian(bdata_writer, data.data(), data.size_in_bytes());
|
|
}
|
|
|
|
[[nodiscard]] static bool read_bdata_simple_gspan(const BDataReader &bdata_reader,
|
|
const DictionaryValue &io_data,
|
|
GMutableSpan r_data)
|
|
{
|
|
const CPPType &type = r_data.type();
|
|
BLI_assert(type.is_trivial());
|
|
if (type.size() == 1 || type.is<ColorGeometry4b>()) {
|
|
return read_bdata_raw_bytes(bdata_reader, io_data, r_data.size_in_bytes(), r_data.data());
|
|
}
|
|
if (type.is_any<int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float>()) {
|
|
return read_bdata_raw_data_with_endian(
|
|
bdata_reader, io_data, type.size(), r_data.size(), r_data.data());
|
|
}
|
|
if (type.is_any<float2, int2>()) {
|
|
return read_bdata_raw_data_with_endian(
|
|
bdata_reader, io_data, sizeof(int32_t), r_data.size() * 2, r_data.data());
|
|
}
|
|
if (type.is<float3>()) {
|
|
return read_bdata_raw_data_with_endian(
|
|
bdata_reader, io_data, sizeof(float), r_data.size() * 3, r_data.data());
|
|
}
|
|
if (type.is<float4x4>()) {
|
|
return read_bdata_raw_data_with_endian(
|
|
bdata_reader, io_data, sizeof(float), r_data.size() * 16, r_data.data());
|
|
}
|
|
if (type.is<ColorGeometry4f>()) {
|
|
return read_bdata_raw_data_with_endian(
|
|
bdata_reader, io_data, sizeof(float), r_data.size() * 4, r_data.data());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static std::shared_ptr<DictionaryValue> write_bdata_shared_simple_gspan(
|
|
BDataWriter &bdata_writer,
|
|
BDataSharing &bdata_sharing,
|
|
const GSpan data,
|
|
const ImplicitSharingInfo *sharing_info)
|
|
{
|
|
return bdata_sharing.write_shared(
|
|
sharing_info, [&]() { return write_bdata_simple_gspan(bdata_writer, data); });
|
|
}
|
|
|
|
[[nodiscard]] static const void *read_bdata_shared_simple_gspan(
|
|
const DictionaryValue &io_data,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing,
|
|
const CPPType &cpp_type,
|
|
const int size,
|
|
const ImplicitSharingInfo **r_sharing_info)
|
|
{
|
|
const std::optional<ImplicitSharingInfoAndData> sharing_info_and_data =
|
|
bdata_sharing.read_shared(io_data, [&]() -> std::optional<ImplicitSharingInfoAndData> {
|
|
void *data_mem = MEM_mallocN_aligned(
|
|
size * cpp_type.size(), cpp_type.alignment(), __func__);
|
|
if (!read_bdata_simple_gspan(bdata_reader, io_data, {cpp_type, data_mem, size})) {
|
|
MEM_freeN(data_mem);
|
|
return std::nullopt;
|
|
}
|
|
return ImplicitSharingInfoAndData{implicit_sharing::info_for_mem_free(data_mem), data_mem};
|
|
});
|
|
if (!sharing_info_and_data) {
|
|
*r_sharing_info = nullptr;
|
|
return nullptr;
|
|
}
|
|
*r_sharing_info = sharing_info_and_data->sharing_info;
|
|
return sharing_info_and_data->data;
|
|
}
|
|
|
|
template<typename T>
|
|
[[nodiscard]] static bool read_bdata_shared_simple_span(const DictionaryValue &io_data,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing,
|
|
const int size,
|
|
T **r_data,
|
|
const ImplicitSharingInfo **r_sharing_info)
|
|
{
|
|
*r_data = const_cast<T *>(static_cast<const T *>(read_bdata_shared_simple_gspan(
|
|
io_data, bdata_reader, bdata_sharing, CPPType::get<T>(), size, r_sharing_info)));
|
|
return *r_data != nullptr;
|
|
}
|
|
|
|
[[nodiscard]] static bool load_attributes(const io::serialize::ArrayValue &io_attributes,
|
|
bke::MutableAttributeAccessor &attributes,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing)
|
|
{
|
|
for (const auto &io_attribute_value : io_attributes.elements()) {
|
|
const auto *io_attribute = io_attribute_value->as_dictionary_value();
|
|
if (!io_attribute) {
|
|
return false;
|
|
}
|
|
const std::optional<StringRefNull> name = io_attribute->lookup_str("name");
|
|
const std::optional<StringRefNull> domain_str = io_attribute->lookup_str("domain");
|
|
const std::optional<StringRefNull> type_str = io_attribute->lookup_str("type");
|
|
auto io_data = io_attribute->lookup_dict("data");
|
|
if (!name || !domain_str || !type_str || !io_data) {
|
|
return false;
|
|
}
|
|
|
|
const std::optional<eAttrDomain> domain = get_domain_from_io_name(*domain_str);
|
|
const std::optional<eCustomDataType> data_type = get_data_type_from_io_name(*type_str);
|
|
if (!domain || !data_type) {
|
|
return false;
|
|
}
|
|
const CPPType *cpp_type = custom_data_type_to_cpp_type(*data_type);
|
|
if (!cpp_type) {
|
|
return false;
|
|
}
|
|
const int domain_size = attributes.domain_size(*domain);
|
|
const ImplicitSharingInfo *attribute_sharing_info;
|
|
const void *attribute_data = read_bdata_shared_simple_gspan(
|
|
*io_data, bdata_reader, bdata_sharing, *cpp_type, domain_size, &attribute_sharing_info);
|
|
if (!attribute_data) {
|
|
return false;
|
|
}
|
|
BLI_SCOPED_DEFER([&]() { attribute_sharing_info->remove_user_and_delete_if_last(); });
|
|
|
|
if (attributes.contains(*name)) {
|
|
/* If the attribute exists already, copy the values over to the existing array. */
|
|
bke::GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_only_span(
|
|
*name, *domain, *data_type);
|
|
if (!attribute) {
|
|
return false;
|
|
}
|
|
cpp_type->copy_assign_n(attribute_data, attribute.span.data(), domain_size);
|
|
attribute.finish();
|
|
}
|
|
else {
|
|
/* Add a new attribute that shares the data. */
|
|
if (!attributes.add(*name,
|
|
*domain,
|
|
*data_type,
|
|
AttributeInitShared(attribute_data, *attribute_sharing_info)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static PointCloud *try_load_pointcloud(const DictionaryValue &io_geometry,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing)
|
|
{
|
|
const DictionaryValue *io_pointcloud = io_geometry.lookup_dict("pointcloud");
|
|
if (!io_pointcloud) {
|
|
return nullptr;
|
|
}
|
|
const io::serialize::ArrayValue *io_attributes = io_pointcloud->lookup_array("attributes");
|
|
if (!io_attributes) {
|
|
return nullptr;
|
|
}
|
|
PointCloud *pointcloud = BKE_pointcloud_new_nomain(0);
|
|
CustomData_free_layer_named(&pointcloud->pdata, "position", 0);
|
|
pointcloud->totpoint = io_pointcloud->lookup_int("num_points").value_or(0);
|
|
|
|
auto cancel = [&]() {
|
|
BKE_id_free(nullptr, pointcloud);
|
|
return nullptr;
|
|
};
|
|
|
|
bke::MutableAttributeAccessor attributes = pointcloud->attributes_for_write();
|
|
if (!load_attributes(*io_attributes, attributes, bdata_reader, bdata_sharing)) {
|
|
return cancel();
|
|
}
|
|
return pointcloud;
|
|
}
|
|
|
|
static Curves *try_load_curves(const DictionaryValue &io_geometry,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing)
|
|
{
|
|
const DictionaryValue *io_curves = io_geometry.lookup_dict("curves");
|
|
if (!io_curves) {
|
|
return nullptr;
|
|
}
|
|
|
|
const io::serialize::ArrayValue *io_attributes = io_curves->lookup_array("attributes");
|
|
if (!io_attributes) {
|
|
return nullptr;
|
|
}
|
|
|
|
Curves *curves_id = bke::curves_new_nomain(0, 0);
|
|
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
|
|
CustomData_free_layer_named(&curves.point_data, "position", 0);
|
|
curves.point_num = io_curves->lookup_int("num_points").value_or(0);
|
|
curves.curve_num = io_curves->lookup_int("num_curves").value_or(0);
|
|
|
|
auto cancel = [&]() {
|
|
BKE_id_free(nullptr, curves_id);
|
|
return nullptr;
|
|
};
|
|
|
|
if (curves.curves_num() > 0) {
|
|
const auto io_curve_offsets = io_curves->lookup_dict("curve_offsets");
|
|
if (!io_curve_offsets) {
|
|
return cancel();
|
|
}
|
|
if (!read_bdata_shared_simple_span(*io_curve_offsets,
|
|
bdata_reader,
|
|
bdata_sharing,
|
|
curves.curves_num() + 1,
|
|
&curves.curve_offsets,
|
|
&curves.runtime->curve_offsets_sharing_info))
|
|
{
|
|
return cancel();
|
|
}
|
|
}
|
|
|
|
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
|
if (!load_attributes(*io_attributes, attributes, bdata_reader, bdata_sharing)) {
|
|
return cancel();
|
|
}
|
|
|
|
return curves_id;
|
|
}
|
|
|
|
static Mesh *try_load_mesh(const DictionaryValue &io_geometry,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing)
|
|
{
|
|
const DictionaryValue *io_mesh = io_geometry.lookup_dict("mesh");
|
|
if (!io_mesh) {
|
|
return nullptr;
|
|
}
|
|
|
|
const io::serialize::ArrayValue *io_attributes = io_mesh->lookup_array("attributes");
|
|
if (!io_attributes) {
|
|
return nullptr;
|
|
}
|
|
|
|
Mesh *mesh = BKE_mesh_new_nomain(0, 0, 0, 0);
|
|
CustomData_free_layer_named(&mesh->vdata, "position", 0);
|
|
CustomData_free_layer_named(&mesh->edata, ".edge_verts", 0);
|
|
CustomData_free_layer_named(&mesh->ldata, ".corner_vert", 0);
|
|
CustomData_free_layer_named(&mesh->ldata, ".corner_edge", 0);
|
|
mesh->totvert = io_mesh->lookup_int("num_vertices").value_or(0);
|
|
mesh->totedge = io_mesh->lookup_int("num_edges").value_or(0);
|
|
mesh->totpoly = io_mesh->lookup_int("num_polygons").value_or(0);
|
|
mesh->totloop = io_mesh->lookup_int("num_corners").value_or(0);
|
|
|
|
auto cancel = [&]() {
|
|
BKE_id_free(nullptr, mesh);
|
|
return nullptr;
|
|
};
|
|
|
|
if (mesh->totpoly > 0) {
|
|
const auto io_poly_offsets = io_mesh->lookup_dict("poly_offsets");
|
|
if (!io_poly_offsets) {
|
|
return cancel();
|
|
}
|
|
if (!read_bdata_shared_simple_span(*io_poly_offsets,
|
|
bdata_reader,
|
|
bdata_sharing,
|
|
mesh->totpoly + 1,
|
|
&mesh->poly_offset_indices,
|
|
&mesh->runtime->poly_offsets_sharing_info))
|
|
{
|
|
return cancel();
|
|
}
|
|
}
|
|
|
|
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
|
if (!load_attributes(*io_attributes, attributes, bdata_reader, bdata_sharing)) {
|
|
return cancel();
|
|
}
|
|
|
|
return mesh;
|
|
}
|
|
|
|
static GeometrySet load_geometry(const DictionaryValue &io_geometry,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing);
|
|
|
|
static std::unique_ptr<bke::Instances> try_load_instances(const DictionaryValue &io_geometry,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing)
|
|
{
|
|
const DictionaryValue *io_instances = io_geometry.lookup_dict("instances");
|
|
if (!io_instances) {
|
|
return nullptr;
|
|
}
|
|
const int num_instances = io_instances->lookup_int("num_instances").value_or(0);
|
|
if (num_instances == 0) {
|
|
return nullptr;
|
|
}
|
|
const io::serialize::ArrayValue *io_attributes = io_instances->lookup_array("attributes");
|
|
if (!io_attributes) {
|
|
return nullptr;
|
|
}
|
|
const io::serialize::ArrayValue *io_references = io_instances->lookup_array("references");
|
|
if (!io_references) {
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
|
|
instances->resize(num_instances);
|
|
|
|
for (const auto &io_reference_value : io_references->elements()) {
|
|
const DictionaryValue *io_reference = io_reference_value->as_dictionary_value();
|
|
GeometrySet reference_geometry;
|
|
if (io_reference) {
|
|
reference_geometry = load_geometry(*io_reference, bdata_reader, bdata_sharing);
|
|
}
|
|
instances->add_reference(std::move(reference_geometry));
|
|
}
|
|
|
|
const auto io_transforms = io_instances->lookup_dict("transforms");
|
|
if (!io_transforms) {
|
|
return {};
|
|
}
|
|
if (!read_bdata_simple_gspan(bdata_reader, *io_transforms, instances->transforms())) {
|
|
return {};
|
|
}
|
|
|
|
const auto io_handles = io_instances->lookup_dict("handles");
|
|
if (!io_handles) {
|
|
return {};
|
|
}
|
|
if (!read_bdata_simple_gspan(bdata_reader, *io_handles, instances->reference_handles())) {
|
|
return {};
|
|
}
|
|
|
|
bke::MutableAttributeAccessor attributes = instances->attributes_for_write();
|
|
if (!load_attributes(*io_attributes, attributes, bdata_reader, bdata_sharing)) {
|
|
return {};
|
|
}
|
|
|
|
return instances;
|
|
}
|
|
|
|
static GeometrySet load_geometry(const DictionaryValue &io_geometry,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing)
|
|
{
|
|
GeometrySet geometry;
|
|
geometry.replace_mesh(try_load_mesh(io_geometry, bdata_reader, bdata_sharing));
|
|
geometry.replace_pointcloud(try_load_pointcloud(io_geometry, bdata_reader, bdata_sharing));
|
|
geometry.replace_curves(try_load_curves(io_geometry, bdata_reader, bdata_sharing));
|
|
geometry.replace_instances(
|
|
try_load_instances(io_geometry, bdata_reader, bdata_sharing).release());
|
|
return geometry;
|
|
}
|
|
|
|
static std::shared_ptr<io::serialize::ArrayValue> serialize_material_slots(
|
|
const Span<const Material *> material_slots)
|
|
{
|
|
auto io_materials = std::make_shared<io::serialize::ArrayValue>();
|
|
for (const Material *material : material_slots) {
|
|
if (material == nullptr) {
|
|
io_materials->append_null();
|
|
}
|
|
else {
|
|
auto io_material = io_materials->append_dict();
|
|
io_material->append_str("name", material->id.name + 2);
|
|
if (material->id.lib != nullptr) {
|
|
io_material->append_str("lib_name", material->id.lib->id.name + 2);
|
|
}
|
|
}
|
|
}
|
|
return io_materials;
|
|
}
|
|
|
|
static std::shared_ptr<io::serialize::ArrayValue> serialize_attributes(
|
|
const bke::AttributeAccessor &attributes,
|
|
BDataWriter &bdata_writer,
|
|
BDataSharing &bdata_sharing,
|
|
const Set<std::string> &attributes_to_ignore)
|
|
{
|
|
auto io_attributes = std::make_shared<io::serialize::ArrayValue>();
|
|
attributes.for_all(
|
|
[&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data) {
|
|
BLI_assert(!attribute_id.is_anonymous());
|
|
if (attributes_to_ignore.contains_as(attribute_id.name())) {
|
|
return true;
|
|
}
|
|
|
|
auto io_attribute = io_attributes->append_dict();
|
|
|
|
io_attribute->append_str("name", attribute_id.name());
|
|
|
|
const StringRefNull domain_name = get_domain_io_name(meta_data.domain);
|
|
io_attribute->append_str("domain", domain_name);
|
|
|
|
const StringRefNull type_name = get_data_type_io_name(meta_data.data_type);
|
|
io_attribute->append_str("type", type_name);
|
|
|
|
const bke::GAttributeReader attribute = attributes.lookup(attribute_id);
|
|
const GVArraySpan attribute_span(attribute.varray);
|
|
io_attribute->append("data",
|
|
write_bdata_shared_simple_gspan(
|
|
bdata_writer,
|
|
bdata_sharing,
|
|
attribute_span,
|
|
attribute.varray.is_span() ? attribute.sharing_info : nullptr));
|
|
return true;
|
|
});
|
|
return io_attributes;
|
|
}
|
|
|
|
static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet &geometry,
|
|
BDataWriter &bdata_writer,
|
|
BDataSharing &bdata_sharing)
|
|
{
|
|
auto io_geometry = std::make_shared<DictionaryValue>();
|
|
if (geometry.has_mesh()) {
|
|
const Mesh &mesh = *geometry.get_mesh_for_read();
|
|
auto io_mesh = io_geometry->append_dict("mesh");
|
|
|
|
io_mesh->append_int("num_vertices", mesh.totvert);
|
|
io_mesh->append_int("num_edges", mesh.totedge);
|
|
io_mesh->append_int("num_polygons", mesh.totpoly);
|
|
io_mesh->append_int("num_corners", mesh.totloop);
|
|
|
|
if (mesh.totpoly > 0) {
|
|
io_mesh->append("poly_offsets",
|
|
write_bdata_shared_simple_gspan(bdata_writer,
|
|
bdata_sharing,
|
|
mesh.poly_offsets(),
|
|
mesh.runtime->poly_offsets_sharing_info));
|
|
}
|
|
|
|
auto io_materials = serialize_material_slots({mesh.mat, mesh.totcol});
|
|
io_mesh->append("materials", io_materials);
|
|
|
|
auto io_attributes = serialize_attributes(mesh.attributes(), bdata_writer, bdata_sharing, {});
|
|
io_mesh->append("attributes", io_attributes);
|
|
}
|
|
if (geometry.has_pointcloud()) {
|
|
const PointCloud &pointcloud = *geometry.get_pointcloud_for_read();
|
|
auto io_pointcloud = io_geometry->append_dict("pointcloud");
|
|
|
|
io_pointcloud->append_int("num_points", pointcloud.totpoint);
|
|
|
|
auto io_materials = serialize_material_slots({pointcloud.mat, pointcloud.totcol});
|
|
io_pointcloud->append("materials", io_materials);
|
|
|
|
auto io_attributes = serialize_attributes(
|
|
pointcloud.attributes(), bdata_writer, bdata_sharing, {});
|
|
io_pointcloud->append("attributes", io_attributes);
|
|
}
|
|
if (geometry.has_curves()) {
|
|
const Curves &curves_id = *geometry.get_curves_for_read();
|
|
const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
|
|
|
|
auto io_curves = io_geometry->append_dict("curves");
|
|
|
|
io_curves->append_int("num_points", curves.point_num);
|
|
io_curves->append_int("num_curves", curves.curve_num);
|
|
|
|
if (curves.curve_num > 0) {
|
|
io_curves->append(
|
|
"curve_offsets",
|
|
write_bdata_shared_simple_gspan(bdata_writer,
|
|
bdata_sharing,
|
|
curves.offsets(),
|
|
curves.runtime->curve_offsets_sharing_info));
|
|
}
|
|
|
|
auto io_materials = serialize_material_slots({curves_id.mat, curves_id.totcol});
|
|
io_curves->append("materials", io_materials);
|
|
|
|
auto io_attributes = serialize_attributes(
|
|
curves.attributes(), bdata_writer, bdata_sharing, {});
|
|
io_curves->append("attributes", io_attributes);
|
|
}
|
|
if (geometry.has_instances()) {
|
|
const bke::Instances &instances = *geometry.get_instances_for_read();
|
|
auto io_instances = io_geometry->append_dict("instances");
|
|
|
|
io_instances->append_int("num_instances", instances.instances_num());
|
|
|
|
auto io_references = io_instances->append_array("references");
|
|
for (const bke::InstanceReference &reference : instances.references()) {
|
|
BLI_assert(reference.type() == bke::InstanceReference::Type::GeometrySet);
|
|
io_references->append(
|
|
serialize_geometry_set(reference.geometry_set(), bdata_writer, bdata_sharing));
|
|
}
|
|
|
|
io_instances->append("transforms",
|
|
write_bdata_simple_gspan(bdata_writer, instances.transforms()));
|
|
io_instances->append("handles",
|
|
write_bdata_simple_gspan(bdata_writer, instances.reference_handles()));
|
|
|
|
auto io_attributes = serialize_attributes(
|
|
instances.attributes(), bdata_writer, bdata_sharing, {"position"});
|
|
io_instances->append("attributes", io_attributes);
|
|
}
|
|
return io_geometry;
|
|
}
|
|
|
|
static std::shared_ptr<io::serialize::ArrayValue> serialize_float_array(const Span<float> values)
|
|
{
|
|
auto io_value = std::make_shared<io::serialize::ArrayValue>();
|
|
for (const float value : values) {
|
|
io_value->append_double(value);
|
|
}
|
|
return io_value;
|
|
}
|
|
|
|
static std::shared_ptr<io::serialize::ArrayValue> serialize_int_array(const Span<int> values)
|
|
{
|
|
auto io_value = std::make_shared<io::serialize::ArrayValue>();
|
|
for (const int value : values) {
|
|
io_value->append_int(value);
|
|
}
|
|
return io_value;
|
|
}
|
|
|
|
static std::shared_ptr<io::serialize::Value> serialize_primitive_value(
|
|
const eCustomDataType data_type, const void *value_ptr)
|
|
{
|
|
switch (data_type) {
|
|
case CD_PROP_FLOAT: {
|
|
const float value = *static_cast<const float *>(value_ptr);
|
|
return std::make_shared<io::serialize::DoubleValue>(value);
|
|
}
|
|
case CD_PROP_FLOAT2: {
|
|
const float2 value = *static_cast<const float2 *>(value_ptr);
|
|
return serialize_float_array({&value.x, 2});
|
|
}
|
|
case CD_PROP_FLOAT3: {
|
|
const float3 value = *static_cast<const float3 *>(value_ptr);
|
|
return serialize_float_array({&value.x, 3});
|
|
}
|
|
case CD_PROP_BOOL: {
|
|
const bool value = *static_cast<const bool *>(value_ptr);
|
|
return std::make_shared<io::serialize::BooleanValue>(value);
|
|
}
|
|
case CD_PROP_INT32: {
|
|
const int value = *static_cast<const int *>(value_ptr);
|
|
return std::make_shared<io::serialize::IntValue>(value);
|
|
}
|
|
case CD_PROP_INT32_2D: {
|
|
const int2 value = *static_cast<const int2 *>(value_ptr);
|
|
return serialize_int_array({&value.x, 2});
|
|
}
|
|
case CD_PROP_BYTE_COLOR: {
|
|
const ColorGeometry4b value = *static_cast<const ColorGeometry4b *>(value_ptr);
|
|
const int4 value_int{&value.r};
|
|
return serialize_int_array({&value_int.x, 4});
|
|
}
|
|
case CD_PROP_COLOR: {
|
|
const ColorGeometry4f value = *static_cast<const ColorGeometry4f *>(value_ptr);
|
|
return serialize_float_array({&value.r, 4});
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
BLI_assert_unreachable();
|
|
return {};
|
|
}
|
|
|
|
void serialize_modifier_simulation_state(const ModifierSimulationState &state,
|
|
BDataWriter &bdata_writer,
|
|
BDataSharing &bdata_sharing,
|
|
DictionaryValue &r_io_root)
|
|
{
|
|
r_io_root.append_int("version", 1);
|
|
auto io_zones = r_io_root.append_array("zones");
|
|
|
|
for (const auto item : state.zone_states_.items()) {
|
|
const SimulationZoneID &zone_id = item.key;
|
|
const SimulationZoneState &zone_state = *item.value;
|
|
|
|
auto io_zone = io_zones->append_dict();
|
|
|
|
auto io_zone_id = io_zone->append_array("zone_id");
|
|
|
|
for (const int node_id : zone_id.node_ids) {
|
|
io_zone_id->append_int(node_id);
|
|
}
|
|
|
|
auto io_state_items = io_zone->append_array("state_items");
|
|
for (const MapItem<int, std::unique_ptr<SimulationStateItem>> &state_item_with_id :
|
|
zone_state.item_by_identifier.items())
|
|
{
|
|
auto io_state_item = io_state_items->append_dict();
|
|
|
|
io_state_item->append_int("id", state_item_with_id.key);
|
|
|
|
if (const GeometrySimulationStateItem *geometry_state_item =
|
|
dynamic_cast<const GeometrySimulationStateItem *>(state_item_with_id.value.get()))
|
|
{
|
|
io_state_item->append_str("type", "GEOMETRY");
|
|
|
|
const GeometrySet &geometry = geometry_state_item->geometry;
|
|
auto io_geometry = serialize_geometry_set(geometry, bdata_writer, bdata_sharing);
|
|
io_state_item->append("data", io_geometry);
|
|
}
|
|
else if (const AttributeSimulationStateItem *attribute_state_item =
|
|
dynamic_cast<const AttributeSimulationStateItem *>(
|
|
state_item_with_id.value.get()))
|
|
{
|
|
io_state_item->append_str("type", "ATTRIBUTE");
|
|
io_state_item->append_str("name", attribute_state_item->name());
|
|
}
|
|
else if (const StringSimulationStateItem *string_state_item =
|
|
dynamic_cast<const StringSimulationStateItem *>(state_item_with_id.value.get()))
|
|
{
|
|
io_state_item->append_str("type", "STRING");
|
|
const StringRefNull str = string_state_item->value();
|
|
/* Small strings are inlined, larger strings are stored separately. */
|
|
const int64_t bdata_threshold = 100;
|
|
if (str.size() < bdata_threshold) {
|
|
io_state_item->append_str("data", string_state_item->value());
|
|
}
|
|
else {
|
|
io_state_item->append("data",
|
|
write_bdata_raw_bytes(bdata_writer, str.data(), str.size()));
|
|
}
|
|
}
|
|
else if (const PrimitiveSimulationStateItem *primitive_state_item =
|
|
dynamic_cast<const PrimitiveSimulationStateItem *>(
|
|
state_item_with_id.value.get()))
|
|
{
|
|
const eCustomDataType data_type = cpp_type_to_custom_data_type(
|
|
primitive_state_item->type());
|
|
io_state_item->append_str("type", get_data_type_io_name(data_type));
|
|
auto io_data = serialize_primitive_value(data_type, primitive_state_item->value());
|
|
io_state_item->append("data", std::move(io_data));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
[[nodiscard]] static bool deserialize_typed_array(
|
|
const io::serialize::Value &io_value,
|
|
FunctionRef<std::optional<T>(const io::serialize::Value &io_element)> fn,
|
|
MutableSpan<T> r_values)
|
|
{
|
|
const io::serialize::ArrayValue *io_array = io_value.as_array_value();
|
|
if (!io_array) {
|
|
return false;
|
|
}
|
|
if (io_array->elements().size() != r_values.size()) {
|
|
return false;
|
|
}
|
|
for (const int i : r_values.index_range()) {
|
|
const io::serialize::Value &io_element = *io_array->elements()[i];
|
|
std::optional<T> element = fn(io_element);
|
|
if (!element) {
|
|
return false;
|
|
}
|
|
r_values[i] = std::move(*element);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<typename T> static std::optional<T> deserialize_int(const io::serialize::Value &io_value)
|
|
{
|
|
const io::serialize::IntValue *io_int = io_value.as_int_value();
|
|
if (!io_int) {
|
|
return std::nullopt;
|
|
}
|
|
const int64_t value = io_int->value();
|
|
if (value < std::numeric_limits<T>::min()) {
|
|
return std::nullopt;
|
|
}
|
|
if (value > std::numeric_limits<T>::max()) {
|
|
return std::nullopt;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static std::optional<float> deserialize_float(const io::serialize::Value &io_value)
|
|
{
|
|
if (const io::serialize::DoubleValue *io_double = io_value.as_double_value()) {
|
|
return io_double->value();
|
|
}
|
|
if (const io::serialize::IntValue *io_int = io_value.as_int_value()) {
|
|
return io_int->value();
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
[[nodiscard]] static bool deserialize_float_array(const io::serialize::Value &io_value,
|
|
MutableSpan<float> r_values)
|
|
{
|
|
return deserialize_typed_array<float>(io_value, deserialize_float, r_values);
|
|
}
|
|
|
|
template<typename T>
|
|
[[nodiscard]] static bool deserialize_int_array(const io::serialize::Value &io_value,
|
|
MutableSpan<T> r_values)
|
|
{
|
|
static_assert(std::is_integral_v<T>);
|
|
return deserialize_typed_array<T>(io_value, deserialize_int<T>, r_values);
|
|
}
|
|
|
|
[[nodiscard]] static bool deserialize_primitive_value(const io::serialize::Value &io_value,
|
|
const eCustomDataType type,
|
|
void *r_value)
|
|
{
|
|
switch (type) {
|
|
case CD_PROP_FLOAT: {
|
|
const std::optional<float> value = deserialize_float(io_value);
|
|
if (!value) {
|
|
return false;
|
|
}
|
|
*static_cast<float *>(r_value) = *value;
|
|
return true;
|
|
}
|
|
case CD_PROP_FLOAT2: {
|
|
return deserialize_float_array(io_value, {static_cast<float *>(r_value), 2});
|
|
}
|
|
case CD_PROP_FLOAT3: {
|
|
return deserialize_float_array(io_value, {static_cast<float *>(r_value), 3});
|
|
}
|
|
case CD_PROP_BOOL: {
|
|
if (const io::serialize::BooleanValue *io_value_boolean = io_value.as_boolean_value()) {
|
|
*static_cast<bool *>(r_value) = io_value_boolean->value();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CD_PROP_INT32: {
|
|
const std::optional<int> value = deserialize_int<int>(io_value);
|
|
if (!value) {
|
|
return false;
|
|
}
|
|
*static_cast<int *>(r_value) = *value;
|
|
return true;
|
|
}
|
|
case CD_PROP_INT32_2D: {
|
|
return deserialize_int_array<int>(io_value, {static_cast<int *>(r_value), 2});
|
|
}
|
|
case CD_PROP_BYTE_COLOR: {
|
|
return deserialize_int_array<uint8_t>(io_value, {static_cast<uint8_t *>(r_value), 4});
|
|
}
|
|
case CD_PROP_COLOR: {
|
|
return deserialize_float_array(io_value, {static_cast<float *>(r_value), 4});
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void deserialize_modifier_simulation_state(const DictionaryValue &io_root,
|
|
const BDataReader &bdata_reader,
|
|
const BDataSharing &bdata_sharing,
|
|
ModifierSimulationState &r_state)
|
|
{
|
|
io::serialize::JsonFormatter formatter;
|
|
const std::optional<int> version = io_root.lookup_int("version");
|
|
if (!version) {
|
|
return;
|
|
}
|
|
if (*version != 1) {
|
|
return;
|
|
}
|
|
const io::serialize::ArrayValue *io_zones = io_root.lookup_array("zones");
|
|
if (!io_zones) {
|
|
return;
|
|
}
|
|
for (const auto &io_zone_value : io_zones->elements()) {
|
|
const DictionaryValue *io_zone = io_zone_value->as_dictionary_value();
|
|
if (!io_zone) {
|
|
continue;
|
|
}
|
|
const io::serialize::ArrayValue *io_zone_id = io_zone->lookup_array("zone_id");
|
|
bke::sim::SimulationZoneID zone_id;
|
|
for (const auto &io_zone_id_element : io_zone_id->elements()) {
|
|
const io::serialize::IntValue *io_node_id = io_zone_id_element->as_int_value();
|
|
if (!io_node_id) {
|
|
continue;
|
|
}
|
|
zone_id.node_ids.append(io_node_id->value());
|
|
}
|
|
|
|
const io::serialize::ArrayValue *io_state_items = io_zone->lookup_array("state_items");
|
|
if (!io_state_items) {
|
|
continue;
|
|
}
|
|
|
|
auto zone_state = std::make_unique<bke::sim::SimulationZoneState>();
|
|
|
|
for (const auto &io_state_item_value : io_state_items->elements()) {
|
|
const DictionaryValue *io_state_item = io_state_item_value->as_dictionary_value();
|
|
if (!io_state_item) {
|
|
continue;
|
|
}
|
|
const std::optional<int> state_item_id = io_state_item->lookup_int("id");
|
|
if (!state_item_id) {
|
|
continue;
|
|
}
|
|
const std::optional<StringRefNull> state_item_type = io_state_item->lookup_str("type");
|
|
if (!state_item_type) {
|
|
continue;
|
|
}
|
|
std::unique_ptr<SimulationStateItem> new_state_item;
|
|
if (*state_item_type == StringRef("GEOMETRY")) {
|
|
const DictionaryValue *io_geometry = io_state_item->lookup_dict("data");
|
|
if (!io_geometry) {
|
|
continue;
|
|
}
|
|
GeometrySet geometry = load_geometry(*io_geometry, bdata_reader, bdata_sharing);
|
|
new_state_item = std::make_unique<bke::sim::GeometrySimulationStateItem>(
|
|
std::move(geometry));
|
|
}
|
|
else if (*state_item_type == StringRef("ATTRIBUTE")) {
|
|
const DictionaryValue *io_attribute = io_state_item;
|
|
if (!io_attribute) {
|
|
continue;
|
|
}
|
|
std::optional<StringRefNull> name = io_attribute->lookup_str("name");
|
|
if (!name) {
|
|
continue;
|
|
}
|
|
new_state_item = std::make_unique<AttributeSimulationStateItem>(std::move(*name));
|
|
}
|
|
else if (*state_item_type == StringRef("STRING")) {
|
|
const std::shared_ptr<io::serialize::Value> *io_data = io_state_item->lookup("data");
|
|
if (!io_data) {
|
|
continue;
|
|
}
|
|
if (io_data->get()->type() == io::serialize::eValueType::String) {
|
|
const io::serialize::StringValue &io_string = *io_data->get()->as_string_value();
|
|
new_state_item = std::make_unique<bke::sim::StringSimulationStateItem>(
|
|
io_string.value());
|
|
}
|
|
else if (const io::serialize::DictionaryValue *io_string =
|
|
io_data->get()->as_dictionary_value()) {
|
|
const std::optional<int64_t> size = io_string->lookup_int("size");
|
|
if (!size) {
|
|
continue;
|
|
}
|
|
std::string str;
|
|
str.resize(*size);
|
|
if (!read_bdata_raw_bytes(bdata_reader, *io_string, *size, str.data())) {
|
|
continue;
|
|
}
|
|
new_state_item = std::make_unique<bke::sim::StringSimulationStateItem>(std::move(str));
|
|
}
|
|
}
|
|
else {
|
|
const std::shared_ptr<io::serialize::Value> *io_data = io_state_item->lookup("data");
|
|
if (!io_data) {
|
|
continue;
|
|
}
|
|
const std::optional<eCustomDataType> data_type = get_data_type_from_io_name(
|
|
*state_item_type);
|
|
if (data_type) {
|
|
const CPPType &cpp_type = *custom_data_type_to_cpp_type(*data_type);
|
|
BUFFER_FOR_CPP_TYPE_VALUE(cpp_type, buffer);
|
|
if (!deserialize_primitive_value(**io_data, *data_type, buffer)) {
|
|
continue;
|
|
}
|
|
BLI_SCOPED_DEFER([&]() { cpp_type.destruct(buffer); });
|
|
new_state_item = std::make_unique<PrimitiveSimulationStateItem>(cpp_type, buffer);
|
|
}
|
|
}
|
|
BLI_assert(new_state_item);
|
|
zone_state->item_by_identifier.add(*state_item_id, std::move(new_state_item));
|
|
}
|
|
|
|
r_state.zone_states_.add_overwrite(zone_id, std::move(zone_state));
|
|
}
|
|
}
|
|
|
|
DiskBDataReader::DiskBDataReader(std::string bdata_dir) : bdata_dir_(std::move(bdata_dir)) {}
|
|
|
|
[[nodiscard]] bool DiskBDataReader::read(const BDataSlice &slice, void *r_data) const
|
|
{
|
|
if (slice.range.is_empty()) {
|
|
return true;
|
|
}
|
|
|
|
char bdata_path[FILE_MAX];
|
|
BLI_path_join(bdata_path, sizeof(bdata_path), bdata_dir_.c_str(), slice.name.c_str());
|
|
|
|
std::lock_guard lock{mutex_};
|
|
std::unique_ptr<fstream> &bdata_file = open_input_streams_.lookup_or_add_cb_as(
|
|
bdata_path,
|
|
[&]() { return std::make_unique<fstream>(bdata_path, std::ios::in | std::ios::binary); });
|
|
bdata_file->seekg(slice.range.start());
|
|
bdata_file->read(static_cast<char *>(r_data), slice.range.size());
|
|
if (bdata_file->gcount() != slice.range.size()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
DiskBDataWriter::DiskBDataWriter(std::string bdata_name,
|
|
std::ostream &bdata_file,
|
|
const int64_t current_offset)
|
|
: bdata_name_(std::move(bdata_name)), bdata_file_(bdata_file), current_offset_(current_offset)
|
|
{
|
|
}
|
|
|
|
BDataSlice DiskBDataWriter::write(const void *data, const int64_t size)
|
|
{
|
|
const int64_t old_offset = current_offset_;
|
|
bdata_file_.write(static_cast<const char *>(data), size);
|
|
current_offset_ += size;
|
|
return {bdata_name_, {old_offset, size}};
|
|
}
|
|
|
|
BDataSharing::~BDataSharing()
|
|
{
|
|
for (const ImplicitSharingInfo *sharing_info : stored_by_runtime_.keys()) {
|
|
sharing_info->remove_weak_user_and_delete_if_last();
|
|
}
|
|
for (const ImplicitSharingInfoAndData &value : runtime_by_stored_.values()) {
|
|
if (value.sharing_info) {
|
|
value.sharing_info->remove_user_and_delete_if_last();
|
|
}
|
|
}
|
|
}
|
|
|
|
DictionaryValuePtr BDataSharing::write_shared(const ImplicitSharingInfo *sharing_info,
|
|
FunctionRef<DictionaryValuePtr()> write_fn)
|
|
{
|
|
if (sharing_info == nullptr) {
|
|
return write_fn();
|
|
}
|
|
return stored_by_runtime_.add_or_modify(
|
|
sharing_info,
|
|
/* Create new value. */
|
|
[&](StoredByRuntimeValue *value) {
|
|
new (value) StoredByRuntimeValue();
|
|
value->io_data = write_fn();
|
|
value->sharing_info_version = sharing_info->version();
|
|
sharing_info->add_weak_user();
|
|
return value->io_data;
|
|
},
|
|
/* Potentially modify existing value. */
|
|
[&](StoredByRuntimeValue *value) {
|
|
const int64_t new_version = sharing_info->version();
|
|
BLI_assert(value->sharing_info_version <= new_version);
|
|
if (value->sharing_info_version < new_version) {
|
|
value->io_data = write_fn();
|
|
value->sharing_info_version = new_version;
|
|
}
|
|
return value->io_data;
|
|
});
|
|
}
|
|
|
|
std::optional<ImplicitSharingInfoAndData> BDataSharing::read_shared(
|
|
const DictionaryValue &io_data,
|
|
FunctionRef<std::optional<ImplicitSharingInfoAndData>()> read_fn) const
|
|
{
|
|
std::lock_guard lock{mutex_};
|
|
|
|
io::serialize::JsonFormatter formatter;
|
|
std::stringstream ss;
|
|
formatter.serialize(ss, io_data);
|
|
const std::string key = ss.str();
|
|
|
|
if (const ImplicitSharingInfoAndData *shared_data = runtime_by_stored_.lookup_ptr(key)) {
|
|
shared_data->sharing_info->add_user();
|
|
return *shared_data;
|
|
}
|
|
std::optional<ImplicitSharingInfoAndData> data = read_fn();
|
|
if (!data) {
|
|
return std::nullopt;
|
|
}
|
|
if (data->sharing_info != nullptr) {
|
|
data->sharing_info->add_user();
|
|
runtime_by_stored_.add_new(key, *data);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
} // namespace blender::bke::sim
|