WIP: Prototype: Geometry Nodes: data block references list per node instance #117635

Closed
Jacques Lucke wants to merge 64 commits from JacquesLucke/blender:bake-data-block-map-instance into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
39 changed files with 1158 additions and 115 deletions

View File

@ -0,0 +1,56 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_string_ref.hh"
#include "BLI_struct_equality_utils.hh"
#include "BLI_vector.hh"
#include "DNA_ID_enums.h"
struct ID;
struct NodesModifierDataBlock;
namespace blender::bke::bake {
/**
* Unique weak reference to a data block within a #Main. It's used when caching/baking data-block
* references. Data-block pointers can't be used directly, because they are not stable over time
* and between Blender sessions.
*/
struct BakeDataBlockID {
ID_Type type;
/**
* Name of the data-block, without the type prefix.
*/
std::string id_name;
/**
* Name of the library data-block that the data-block is in. This refers to `Library.id.name` and
* not the file path. The type prefix of the name is omitted. If this is empty, the data-block is
* expected to be local and not linked.
*/
std::string lib_name;
BakeDataBlockID(ID_Type type, std::string id_name, std::string lib_name);
BakeDataBlockID(const ID &id);
BakeDataBlockID(const NodesModifierDataBlock &data_block);
uint64_t hash() const;
friend std::ostream &operator<<(std::ostream &stream, const BakeDataBlockID &id);
BLI_STRUCT_EQUALITY_OPERATORS_3(BakeDataBlockID, type, id_name, lib_name)
};
/**
* A list of weak data-block references for material slots.
*/
struct BakeMaterialsList : public Vector<std::optional<BakeDataBlockID>> {};
} // namespace blender::bke::bake

View File

@ -0,0 +1,43 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <optional>
#include "BLI_string_ref.hh"
#include "BLI_struct_equality_utils.hh"
#include "BKE_bake_data_block_id.hh"
#include "DNA_ID_enums.h"
namespace blender::bke::bake {
/**
* Maps #BakeDataBlockID to the corresponding data-block. This is used during depsgraph evaluation
* to remap weak data-block references stored in baked data to the actual data-blocks at run-time.
*
* Also it keeps track of missing data-blocks, so that they can be added later.
*/
struct BakeDataBlockMap {
public:
/**
* Tries to retrieve the data block for the given key. If it's not explicitly mapped, it might be
* added to the mapping. If it's still not found, null is returned.
*/
virtual ID *lookup_or_remember_missing(const BakeDataBlockID &key) = 0;
/**
* Tries to add the data block to the map. This may not succeed in all cases, e.g. if the
* implementation does not allow inserting new mapping items.
*/
virtual void try_add(ID &id) = 0;
};
} // namespace blender::bke::bake

View File

@ -4,6 +4,7 @@
#pragma once
#include "BKE_bake_data_block_map.hh"
#include "BKE_geometry_set.hh"
namespace blender::bke::bake {
@ -42,13 +43,18 @@ class GeometryBakeItem : public BakeItem {
GeometryBakeItem(GeometrySet geometry);
/**
* Removes parts of the geometry that can't be stored in the simulation state:
* - Anonymous attributes can't be stored because it is not known which of them will or will not
* be used in the future.
* - Materials can't be stored directly, because they are linked ID data blocks that can't be
* restored from baked data currently.
* Removes parts of the geometry that can't be baked/cached (anonymous attributes) and replaces
* data-block pointers with #BakeDataBlockID.
*/
static void cleanup_geometry(GeometrySet &geometry);
static void prepare_geometry_for_bake(GeometrySet &geometry, BakeDataBlockMap *data_block_map);
/**
* The baked data does not have raw pointers to referenced data-blocks because those would become
* dangling quickly. Instead it has weak name-based references (#BakeDataBlockID). This function
* attempts to restore the actual data block pointers based on the weak references using the
* given mapping.
*/
static void try_restore_data_blocks(GeometrySet &geometry, BakeDataBlockMap *data_block_map);
};
/**

View File

@ -35,8 +35,8 @@ struct BakeSocketConfig {
* Create new bake items from the socket values. The socket values are not destructed, but they may
* be in a moved-from state afterwards.
*/
Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(Span<void *> socket_values,
const BakeSocketConfig &config);
Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(
Span<void *> socket_values, const BakeSocketConfig &config, BakeDataBlockMap *data_block_map);
/**
* Create socket values from bake items.
@ -52,6 +52,7 @@ Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(Span<void *> s
void move_bake_items_to_socket_values(
Span<BakeItem *> bake_items,
const BakeSocketConfig &config,
BakeDataBlockMap *data_block_map,
FunctionRef<std::shared_ptr<AnonymousAttributeFieldInput>(int socket_index, const CPPType &)>
make_attribute_field,
Span<void *> r_socket_values);
@ -63,6 +64,7 @@ void move_bake_items_to_socket_values(
void copy_bake_items_to_socket_values(
Span<const BakeItem *> bake_items,
const BakeSocketConfig &config,
BakeDataBlockMap *data_block_map,
FunctionRef<std::shared_ptr<AnonymousAttributeFieldInput>(int, const CPPType &)>
make_attribute_field,
Span<void *> r_socket_values);

View File

@ -33,6 +33,9 @@ class AttributeAccessor;
class MutableAttributeAccessor;
enum class AttrDomain : int8_t;
} // namespace blender::bke
namespace blender::bke::bake {
struct BakeMaterialsList;
}
namespace blender::bke {
@ -111,6 +114,9 @@ class CurvesGeometryRuntime {
/** Normal direction vectors for each evaluated point. */
mutable SharedCache<Vector<float3>> evaluated_normal_cache;
/** Stores weak references to material data blocks. */
std::unique_ptr<bake::BakeMaterialsList> bake_materials;
};
/**

View File

@ -204,6 +204,7 @@ void BKE_libblock_ensure_unique_name(Main *bmain, ID *id) ATTR_NONNULL();
ID *BKE_libblock_find_name(Main *bmain, short type, const char *name) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
ID *BKE_libblock_find_session_uid(Main *bmain, short type, uint32_t session_uid);
ID *BKE_libblock_find_name_and_library(Main *bmain, const char *name, const char *lib_name);
/**
* Duplicate (a.k.a. deep copy) common processing options.
* See also eDupli_ID_Flags for options controlling what kind of IDs to duplicate.

View File

@ -29,6 +29,9 @@ struct SubsurfRuntimeData;
namespace blender::bke {
struct EditMeshData;
}
namespace blender::bke::bake {
struct BakeMaterialsList;
}
/** #MeshRuntime.wrapper_type */
enum eMeshWrapperType {
@ -207,6 +210,9 @@ struct MeshRuntime {
*/
BitVector<> subsurf_optimal_display_edges;
/** Stores weak references to material data blocks. */
std::unique_ptr<bake::BakeMaterialsList> bake_materials;
MeshRuntime();
~MeshRuntime();
};

View File

@ -22,6 +22,9 @@ struct Main;
struct Object;
struct PointCloud;
struct Scene;
namespace blender::bke::bake {
struct BakeMaterialsList;
}
/* PointCloud datablock */
extern const char *POINTCLOUD_ATTR_POSITION;
@ -37,6 +40,9 @@ struct PointCloudRuntime {
*/
mutable SharedCache<Bounds<float3>> bounds_cache;
/** Stores weak references to material data blocks. */
std::unique_ptr<bake::BakeMaterialsList> bake_materials;
MEM_CXX_CLASS_ALLOC_FUNCS("PointCloudRuntime");
};

View File

@ -70,6 +70,7 @@ set(SRC
intern/attribute_math.cc
intern/autoexec.cc
intern/bake_geometry_nodes_modifier.cc
intern/bake_data_block_map.cc
intern/bake_items.cc
intern/bake_items_paths.cc
intern/bake_items_serialize.cc
@ -335,6 +336,7 @@ set(SRC
BKE_attribute_math.hh
BKE_autoexec.hh
BKE_bake_geometry_nodes_modifier.hh
BKE_bake_data_block_map.hh
BKE_bake_items.hh
BKE_bake_items_paths.hh
BKE_bake_items_serialize.hh

View File

@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <ostream>
#include "BLI_hash.hh"
#include "BKE_bake_data_block_map.hh"
#include "DNA_ID.h"
#include "DNA_modifier_types.h"
namespace blender::bke::bake {
BakeDataBlockID::BakeDataBlockID(ID_Type type, std::string id_name, std::string lib_name)
: type(type), id_name(std::move(id_name)), lib_name(std::move(lib_name))
{
}
BakeDataBlockID::BakeDataBlockID(const ID &id)
{
this->type = GS(id.name);
this->id_name = id.name + 2;
if (id.lib) {
this->lib_name = id.lib->id.name + 2;
}
}
BakeDataBlockID::BakeDataBlockID(const NodesModifierDataBlock &data_block)
: BakeDataBlockID(ID_Type(data_block.id_type),
StringRef(data_block.id_name),
StringRef(data_block.lib_name))
{
}
std::ostream &operator<<(std::ostream &stream, const BakeDataBlockID &id)
{
return stream << "(" << id.id_name << ", Lib: " << id.lib_name << ")";
}
uint64_t BakeDataBlockID::hash() const
{
return get_default_hash(this->type, this->id_name, this->lib_name);
}
} // namespace blender::bke::bake

View File

@ -27,27 +27,49 @@ using DictionaryValuePtr = std::shared_ptr<DictionaryValue>;
GeometryBakeItem::GeometryBakeItem(GeometrySet geometry) : geometry(std::move(geometry)) {}
static void remove_materials(Material ***materials, short *materials_num)
static std::unique_ptr<BakeMaterialsList> materials_to_weak_references(
Material ***materials, short *materials_num, BakeDataBlockMap *data_block_map)
{
if (*materials_num == 0) {
return {};
}
auto materials_list = std::make_unique<BakeMaterialsList>();
materials_list->resize(*materials_num);
for (const int i : materials_list->index_range()) {
Material *material = (*materials)[i];
if (material) {
(*materials_list)[i] = BakeDataBlockID(material->id);
if (data_block_map) {
data_block_map->try_add(material->id);
}
}
}
MEM_SAFE_FREE(*materials);
*materials_num = 0;
return materials_list;
}
void GeometryBakeItem::cleanup_geometry(GeometrySet &main_geometry)
void GeometryBakeItem::prepare_geometry_for_bake(GeometrySet &main_geometry,
BakeDataBlockMap *data_block_map)
{
main_geometry.ensure_owns_all_data();
main_geometry.modify_geometry_sets([&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
mesh->attributes_for_write().remove_anonymous();
remove_materials(&mesh->mat, &mesh->totcol);
mesh->runtime->bake_materials = materials_to_weak_references(
&mesh->mat, &mesh->totcol, data_block_map);
}
if (Curves *curves = geometry.get_curves_for_write()) {
curves->geometry.wrap().attributes_for_write().remove_anonymous();
remove_materials(&curves->mat, &curves->totcol);
curves->geometry.runtime->bake_materials = materials_to_weak_references(
&curves->mat, &curves->totcol, data_block_map);
}
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
pointcloud->attributes_for_write().remove_anonymous();
remove_materials(&pointcloud->mat, &pointcloud->totcol);
pointcloud->runtime->bake_materials = materials_to_weak_references(
&pointcloud->mat, &pointcloud->totcol, data_block_map);
}
if (bke::Instances *instances = geometry.get_instances_for_write()) {
instances->attributes_for_write().remove_anonymous();
@ -59,6 +81,53 @@ void GeometryBakeItem::cleanup_geometry(GeometrySet &main_geometry)
});
}
static void restore_materials(Material ***materials,
short *materials_num,
std::unique_ptr<BakeMaterialsList> materials_list,
BakeDataBlockMap *data_block_map)
{
if (!materials_list) {
return;
}
BLI_assert(*materials == nullptr);
*materials_num = materials_list->size();
*materials = MEM_cnew_array<Material *>(materials_list->size(), __func__);
if (!data_block_map) {
return;
}
for (const int i : materials_list->index_range()) {
const std::optional<BakeDataBlockID> &data_block_id = (*materials_list)[i];
if (data_block_id) {
(*materials)[i] = reinterpret_cast<Material *>(
data_block_map->lookup_or_remember_missing(*data_block_id));
}
}
}
void GeometryBakeItem::try_restore_data_blocks(GeometrySet &main_geometry,
BakeDataBlockMap *data_block_map)
{
main_geometry.modify_geometry_sets([&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
restore_materials(
&mesh->mat, &mesh->totcol, std::move(mesh->runtime->bake_materials), data_block_map);
}
if (Curves *curves = geometry.get_curves_for_write()) {
restore_materials(&curves->mat,
&curves->totcol,
std::move(curves->geometry.runtime->bake_materials),
data_block_map);
}
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
restore_materials(&pointcloud->mat,
&pointcloud->totcol,
std::move(pointcloud->runtime->bake_materials),
data_block_map);
}
});
}
PrimitiveBakeItem::PrimitiveBakeItem(const CPPType &type, const void *value) : type_(type)
{
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);

View File

@ -365,6 +365,32 @@ template<typename T>
return *r_data != nullptr;
}
[[nodiscard]] static bool load_materials(const io::serialize::ArrayValue &io_materials,
std::unique_ptr<BakeMaterialsList> &materials)
{
if (io_materials.elements().is_empty()) {
return true;
}
materials = std::make_unique<BakeMaterialsList>();
for (const auto &io_material_value : io_materials.elements()) {
if (io_material_value->type() == io::serialize::eValueType::Null) {
materials->append(std::nullopt);
continue;
}
const auto *io_material = io_material_value->as_dictionary_value();
if (!io_material) {
return false;
}
std::optional<std::string> id_name = io_material->lookup_str("name");
if (!id_name) {
return false;
}
std::string lib_name = io_material->lookup_str("lib_name").value_or("");
materials->append(BakeDataBlockID(ID_MA, std::move(*id_name), std::move(lib_name)));
}
return true;
}
[[nodiscard]] static bool load_attributes(const io::serialize::ArrayValue &io_attributes,
MutableAttributeAccessor &attributes,
const BlobReader &blob_reader,
@ -450,6 +476,12 @@ static PointCloud *try_load_pointcloud(const DictionaryValue &io_geometry,
if (!load_attributes(*io_attributes, attributes, blob_reader, blob_sharing)) {
return cancel();
}
if (const io::serialize::ArrayValue *io_materials = io_pointcloud->lookup_array("materials")) {
if (!load_materials(*io_materials, pointcloud->runtime->bake_materials)) {
return cancel();
}
}
return pointcloud;
}
@ -499,6 +531,12 @@ static Curves *try_load_curves(const DictionaryValue &io_geometry,
return cancel();
}
if (const io::serialize::ArrayValue *io_materials = io_curves->lookup_array("materials")) {
if (!load_materials(*io_materials, curves.runtime->bake_materials)) {
return cancel();
}
}
curves.update_curve_types();
return curves_id;
@ -554,6 +592,12 @@ static Mesh *try_load_mesh(const DictionaryValue &io_geometry,
return cancel();
}
if (const io::serialize::ArrayValue *io_materials = io_mesh->lookup_array("materials")) {
if (!load_materials(*io_materials, mesh->runtime->bake_materials)) {
return cancel();
}
}
return mesh;
}
@ -630,20 +674,23 @@ static GeometrySet load_geometry(const DictionaryValue &io_geometry,
return geometry;
}
static std::shared_ptr<io::serialize::ArrayValue> serialize_material_slots(
const Span<const Material *> material_slots)
static std::shared_ptr<io::serialize::ArrayValue> serialize_materials(
const std::unique_ptr<BakeMaterialsList> &materials)
{
auto io_materials = std::make_shared<io::serialize::ArrayValue>();
for (const Material *material : material_slots) {
if (material == nullptr) {
io_materials->append_null();
if (!materials) {
return io_materials;
}
for (const std::optional<BakeDataBlockID> &material : *materials) {
if (material) {
auto io_material = io_materials->append_dict();
io_material->append_str("name", material->id_name);
if (!material->lib_name.empty()) {
io_material->append_str("lib_name", material->lib_name);
}
}
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);
}
io_materials->append_null();
}
}
return io_materials;
@ -707,7 +754,7 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
mesh.runtime->face_offsets_sharing_info));
}
auto io_materials = serialize_material_slots({mesh.mat, mesh.totcol});
auto io_materials = serialize_materials(mesh.runtime->bake_materials);
io_mesh->append("materials", io_materials);
auto io_attributes = serialize_attributes(mesh.attributes(), blob_writer, blob_sharing, {});
@ -719,7 +766,7 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
io_pointcloud->append_int("num_points", pointcloud.totpoint);
auto io_materials = serialize_material_slots({pointcloud.mat, pointcloud.totcol});
auto io_materials = serialize_materials(pointcloud.runtime->bake_materials);
io_pointcloud->append("materials", io_materials);
auto io_attributes = serialize_attributes(
@ -744,7 +791,7 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
curves.runtime->curve_offsets_sharing_info));
}
auto io_materials = serialize_material_slots({curves_id.mat, curves_id.totcol});
auto io_materials = serialize_materials(curves.runtime->bake_materials);
io_curves->append("materials", io_materials);
auto io_attributes = serialize_attributes(curves.attributes(), blob_writer, blob_sharing, {});

View File

@ -3,16 +3,15 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_bake_items_socket.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_node.hh"
#include "BKE_node_socket_value.hh"
namespace blender::bke::bake {
Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(const Span<void *> socket_values,
const BakeSocketConfig &config)
const BakeSocketConfig &config,
BakeDataBlockMap *data_block_map)
{
BLI_assert(socket_values.size() == config.types.size());
BLI_assert(socket_values.size() == config.geometries_by_attribute.size());
@ -99,7 +98,7 @@ Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(const Span<voi
continue;
}
GeometrySet &geometry = static_cast<GeometryBakeItem *>(bake_items[i].get())->geometry;
GeometryBakeItem::cleanup_geometry(geometry);
GeometryBakeItem::prepare_geometry_for_bake(geometry, data_block_map);
}
return bake_items;
@ -192,6 +191,14 @@ static void rename_attributes(const Span<GeometrySet *> geometries,
}
}
static void restore_data_blocks(const Span<GeometrySet *> geometries,
BakeDataBlockMap *data_block_map)
{
for (GeometrySet *main_geometry : geometries) {
GeometryBakeItem::try_restore_data_blocks(*main_geometry, data_block_map);
}
}
static void default_initialize_socket_value(const eNodeSocketDatatype socket_type, void *r_value)
{
const char *socket_idname = nodeStaticSocketType(socket_type, 0);
@ -203,6 +210,7 @@ static void default_initialize_socket_value(const eNodeSocketDatatype socket_typ
void move_bake_items_to_socket_values(
const Span<BakeItem *> bake_items,
const BakeSocketConfig &config,
BakeDataBlockMap *data_block_map,
FunctionRef<std::shared_ptr<AnonymousAttributeFieldInput>(int, const CPPType &)>
make_attribute_field,
const Span<void *> r_socket_values)
@ -237,11 +245,13 @@ void move_bake_items_to_socket_values(
}
rename_attributes(geometries, attribute_map);
restore_data_blocks(geometries, data_block_map);
}
void copy_bake_items_to_socket_values(
const Span<const BakeItem *> bake_items,
const BakeSocketConfig &config,
BakeDataBlockMap *data_block_map,
FunctionRef<std::shared_ptr<AnonymousAttributeFieldInput>(int, const CPPType &)>
make_attribute_field,
const Span<void *> r_socket_values)
@ -273,6 +283,7 @@ void copy_bake_items_to_socket_values(
}
rename_attributes(geometries, attribute_map);
restore_data_blocks(geometries, data_block_map);
}
} // namespace blender::bke::bake

View File

@ -26,6 +26,7 @@
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
#include "BKE_bake_data_block_id.hh"
#include "BKE_curves.hh"
#include "BKE_curves_utils.hh"
#include "BKE_customdata.hh"
@ -119,6 +120,11 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
dst.runtime->evaluated_length_cache = src.runtime->evaluated_length_cache;
dst.runtime->evaluated_tangent_cache = src.runtime->evaluated_tangent_cache;
dst.runtime->evaluated_normal_cache = src.runtime->evaluated_normal_cache;
if (src.runtime->bake_materials) {
dst.runtime->bake_materials = std::make_unique<bake::BakeMaterialsList>(
*src.runtime->bake_materials);
}
}
CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) : CurvesGeometry()

View File

@ -1489,6 +1489,28 @@ ID *BKE_libblock_find_session_uid(Main *bmain, const short type, const uint32_t
return nullptr;
}
ID *BKE_libblock_find_name_and_library(Main *bmain, const char *name, const char *lib_name)
{
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!STREQ(id->name + 2, name)) {
continue;
}
if (lib_name == nullptr || lib_name[0] == '\0') {
if (id->lib == nullptr) {
return id;
}
return nullptr;
}
if (!STREQ(id->lib->id.name + 2, lib_name)) {
continue;
}
return id;
}
FOREACH_MAIN_ID_END;
return nullptr;
}
void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
{
#define ID_SORT_STEP_SIZE 512

View File

@ -44,6 +44,7 @@
#include "BKE_anim_data.h"
#include "BKE_attribute.hh"
#include "BKE_bake_data_block_id.hh"
#include "BKE_bpath.h"
#include "BKE_deform.h"
#include "BKE_editmesh.hh"
@ -146,6 +147,10 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
mesh_dst->runtime->vert_to_face_map_cache = mesh_src->runtime->vert_to_face_map_cache;
mesh_dst->runtime->vert_to_corner_map_cache = mesh_src->runtime->vert_to_corner_map_cache;
mesh_dst->runtime->corner_to_face_map_cache = mesh_src->runtime->corner_to_face_map_cache;
if (mesh_src->runtime->bake_materials) {
mesh_dst->runtime->bake_materials = std::make_unique<blender::bke::bake::BakeMaterialsList>(
*mesh_src->runtime->bake_materials);
}
/* Only do tessface if we have no faces. */
const bool do_tessface = ((mesh_src->totface_legacy != 0) && (mesh_src->faces_num == 0));

View File

@ -17,6 +17,7 @@
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BKE_bake_data_block_id.hh"
#include "BKE_bvhutils.hh"
#include "BKE_customdata.hh"
#include "BKE_editmesh_cache.hh"

View File

@ -25,6 +25,7 @@
#include "BLI_vector.hh"
#include "BKE_anim_data.h"
#include "BKE_bake_data_block_id.hh"
#include "BKE_customdata.hh"
#include "BKE_geometry_set.hh"
#include "BKE_global.h"
@ -86,6 +87,11 @@ static void pointcloud_copy_data(Main * /*bmain*/,
pointcloud_dst->runtime = new blender::bke::PointCloudRuntime();
pointcloud_dst->runtime->bounds_cache = pointcloud_src->runtime->bounds_cache;
if (pointcloud_src->runtime->bake_materials) {
pointcloud_dst->runtime->bake_materials =
std::make_unique<blender::bke::bake::BakeMaterialsList>(
*pointcloud_src->runtime->bake_materials);
}
pointcloud_dst->batch_cache = nullptr;
}

View File

@ -24,6 +24,7 @@
#include "DNA_mask_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
@ -79,6 +80,7 @@
#include "BKE_linestyle.h"
#include "BKE_main.hh"
#include "BKE_mask.h"
#include "BKE_modifier.hh"
#include "BKE_node.hh"
#include "BKE_node_runtime.hh"
#include "BKE_object.hh"
@ -97,6 +99,7 @@
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_debug.hh"
#include "DEG_depsgraph_query.hh"
#include "DEG_depsgraph_writeback_sync.hh"
#include "RE_engine.h"
@ -2567,7 +2570,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
prepare_mesh_for_viewport_render(bmain, scene, view_layer);
/* Update all objects: drivers, matrices, etc. flags set
* by depsgraph or manual, no layer check here, gets correct flushed. */
DEG_evaluate_on_refresh(depsgraph);
DEG_evaluate_on_refresh(depsgraph, DEG_EVALUATE_SYNC_WRITEBACK_YES);
/* Update sound system. */
BKE_scene_update_sound(depsgraph, bmain);
/* Notify python about depsgraph update. */
@ -2648,10 +2651,10 @@ void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool cle
* lose any possible unkeyed changes made by the handler. */
if (pass == 0) {
const float frame = BKE_scene_frame_get(scene);
DEG_evaluate_on_framechange(depsgraph, frame);
DEG_evaluate_on_framechange(depsgraph, frame, DEG_EVALUATE_SYNC_WRITEBACK_YES);
}
else {
DEG_evaluate_on_refresh(depsgraph);
DEG_evaluate_on_refresh(depsgraph, DEG_EVALUATE_SYNC_WRITEBACK_YES);
}
/* Update sound system animation. */
BKE_scene_update_sound(depsgraph, bmain);

View File

@ -88,6 +88,7 @@ set(SRC
intern/depsgraph_tag.cc
intern/depsgraph_type.cc
intern/depsgraph_update.cc
intern/depsgraph_writeback_sync.cc
DEG_depsgraph.hh
DEG_depsgraph_build.hh
@ -95,6 +96,7 @@ set(SRC
DEG_depsgraph_light_linking.hh
DEG_depsgraph_physics.hh
DEG_depsgraph_query.hh
DEG_depsgraph_writeback_sync.hh
intern/builder/deg_builder.h
intern/builder/deg_builder_cache.h

View File

@ -173,18 +173,32 @@ void DEG_ids_restore_recalc(Depsgraph *depsgraph);
/** \name Graph Evaluation
* \{ */
enum DepsgraphEvaluateSyncWriteback {
DEG_EVALUATE_SYNC_WRITEBACK_NO,
/**
* Allow writing back to original data after depsgraph evaluation. The change to original data
* may add new ID relations and may tag the depsgraph as changed again.
*/
DEG_EVALUATE_SYNC_WRITEBACK_YES,
};
/**
* Frame changed recalculation entry point.
*
* \note The frame-change happened for root scene that graph belongs to.
*/
void DEG_evaluate_on_framechange(Depsgraph *graph, float frame);
void DEG_evaluate_on_framechange(
Depsgraph *graph,
float frame,
DepsgraphEvaluateSyncWriteback sync_writeback = DEG_EVALUATE_SYNC_WRITEBACK_NO);
/**
* Data changed recalculation entry point.
* Evaluate all nodes tagged for updating.
*/
void DEG_evaluate_on_refresh(Depsgraph *graph);
void DEG_evaluate_on_refresh(
Depsgraph *graph,
DepsgraphEvaluateSyncWriteback sync_writeback = DEG_EVALUATE_SYNC_WRITEBACK_NO);
/** \} */

View File

@ -0,0 +1,31 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* This file provides an API that can be used to modify original (as opposed to evaluated)
* data-blocks after depsgraph evaluation. For some data (e.g. animated properties), this is done
* during depsgraph evaluation. However, this is not possible in all cases. For example, if the
* change to the original data adds a new relation between data-blocks, a user-count (#ID.us) has
* to be increased. This counter is not atomic and can therefore not be modified arbitrarily from
* different threads.
*/
#include <functional>
struct Depsgraph;
namespace blender::deg::sync_writeback {
/**
* Add a writeback task during depsgraph evaluation. The given function is called after depsgraph
* evaluation is done if the depsgraph is active. It is allowed to change original data blocks and
* even to add new relations.
*/
void add(Depsgraph &depsgraph, std::function<void()> fn);
} // namespace blender::deg::sync_writeback

View File

@ -14,6 +14,8 @@
#pragma once
#include <functional>
#include <mutex>
#include <stdlib.h>
#include "MEM_guardedalloc.h"
@ -174,6 +176,14 @@ struct Depsgraph {
/* The number of times this graph has been evaluated. */
uint64_t update_count;
/**
* Stores functions that can be called after depsgraph evaluation to writeback some changes to
* original data. Also see `DEG_depsgraph_writeback_sync.hh`.
*/
Vector<std::function<void()>> sync_writeback_callbacks;
/** Needs to be locked when adding a writeback callback during evaluation. */
std::mutex sync_writeback_callbacks_mutex;
MEM_CXX_CLASS_ALLOC_FUNCS("Depsgraph");
};

View File

@ -20,6 +20,7 @@
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "DEG_depsgraph_writeback_sync.hh"
#include "intern/eval/deg_eval.h"
#include "intern/eval/deg_eval_flush.h"
@ -33,7 +34,8 @@
namespace deg = blender::deg;
static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph)
static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph,
const DepsgraphEvaluateSyncWriteback sync_writeback)
{
/* Update the time on the cow scene. */
if (deg_graph->scene_cow) {
@ -43,9 +45,18 @@ static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph)
deg::graph_tag_ids_for_visible_update(deg_graph);
deg::deg_graph_flush_updates(deg_graph);
deg::deg_evaluate_on_refresh(deg_graph);
if (sync_writeback == DEG_EVALUATE_SYNC_WRITEBACK_YES) {
if (deg_graph->is_active) {
for (std::function<void()> &fn : deg_graph->sync_writeback_callbacks) {
fn();
}
}
}
deg_graph->sync_writeback_callbacks.clear();
}
void DEG_evaluate_on_refresh(Depsgraph *graph)
void DEG_evaluate_on_refresh(Depsgraph *graph, const DepsgraphEvaluateSyncWriteback sync_writeback)
{
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
const Scene *scene = DEG_get_input_scene(graph);
@ -66,10 +77,12 @@ void DEG_evaluate_on_refresh(Depsgraph *graph)
deg_graph->tag_time_source();
}
deg_flush_updates_and_refresh(deg_graph);
deg_flush_updates_and_refresh(deg_graph, sync_writeback);
}
void DEG_evaluate_on_framechange(Depsgraph *graph, float frame)
void DEG_evaluate_on_framechange(Depsgraph *graph,
float frame,
const DepsgraphEvaluateSyncWriteback sync_writeback)
{
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
const Scene *scene = DEG_get_input_scene(graph);
@ -77,5 +90,5 @@ void DEG_evaluate_on_framechange(Depsgraph *graph, float frame)
deg_graph->tag_time_source();
deg_graph->frame = frame;
deg_graph->ctime = BKE_scene_frame_to_ctime(scene, frame);
deg_flush_updates_and_refresh(deg_graph);
deg_flush_updates_and_refresh(deg_graph, sync_writeback);
}

View File

@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <mutex>
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_writeback_sync.hh"
#include "BLI_map.hh"
#include "depsgraph.hh"
namespace blender::deg::sync_writeback {
void add(::Depsgraph &depsgraph, std::function<void()> fn)
{
deg::Depsgraph &deg_graph = reinterpret_cast<deg::Depsgraph &>(depsgraph);
if (!deg_graph.is_active) {
return;
}
std::lock_guard lock{deg_graph.sync_writeback_callbacks_mutex};
deg_graph.sync_writeback_callbacks.append(std::move(fn));
}
} // namespace blender::deg::sync_writeback

View File

@ -21,6 +21,7 @@
#include "ED_screen.hh"
#include "DNA_array_utils.hh"
#include "DNA_curves_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
@ -706,6 +707,19 @@ static void try_delete_bake(
else if (auto *node_cache = modifier_cache.bake_cache_by_id.lookup_ptr(bake_id)) {
(*node_cache)->reset();
}
NodesModifierBake *bake = nmd.find_bake(bake_id);
if (!bake) {
return;
}
dna::array::clear<NodesModifierDataBlock>(&bake->data_blocks,
&bake->data_blocks_num,
&bake->active_data_block,
[](NodesModifierDataBlock *data_block) {
MEM_SAFE_FREE(data_block->id_name);
MEM_SAFE_FREE(data_block->lib_name);
id_us_min(data_block->id);
});
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
*bmain, object, nmd, bake_id);
if (!bake_path) {

View File

@ -225,6 +225,7 @@ void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_geometry_nodes_input_attribute_toggle(struct wmOperatorType *ot);
void OBJECT_OT_geometry_node_tree_copy_assign(struct wmOperatorType *ot);
void OBJECT_OT_geometry_nodes_modifier_data_block_remove(struct wmOperatorType *ot);
/* object_gpencil_modifiers.c */

View File

@ -16,6 +16,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_array_utils.hh"
#include "DNA_curve_types.h"
#include "DNA_dynamicpaint_types.h"
#include "DNA_fluid_types.h"
@ -3724,3 +3725,65 @@ void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot)
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Remove Data Block from Geometry Nodes Modifier
* \{ */
static int geometry_nodes_modifier_data_block_remove_exec(bContext *C, wmOperator *op)
{
// Object *ob = ED_object_active_context(C);
// Main *bmain = CTX_data_main(C);
// char modifier_name[MAX_NAME];
// RNA_string_get(op->ptr, "modifier_name", modifier_name);
// NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(
// BKE_modifiers_findby_name(ob, modifier_name));
// if (nmd == nullptr) {
// return OPERATOR_CANCELLED;
// }
// if (nmd->data_blocks_num == 0) {
// return OPERATOR_CANCELLED;
// }
// const int index_to_remove = nmd->active_data_block;
// if (index_to_remove < 0 || index_to_remove >= nmd->data_blocks_num) {
// return OPERATOR_CANCELLED;
// }
// blender::dna::array::remove_index<NodesModifierDataBlock>(
// &nmd->data_blocks,
// &nmd->data_blocks_num,
// &nmd->active_data_block,
// index_to_remove,
// [](NodesModifierDataBlock *data_block) {
// MEM_SAFE_FREE(data_block->id_name);
// MEM_SAFE_FREE(data_block->lib_name);
// if (data_block->id) {
// id_us_min(data_block->id);
// }
// });
// DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
// DEG_relations_tag_update(bmain);
// WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
void OBJECT_OT_geometry_nodes_modifier_data_block_remove(wmOperatorType *ot)
{
ot->name = "Remove Geometry Nodes Modifier Data Block";
ot->description =
"Remove active data block. Note that it might be added right back in case it's used by the "
"node tree";
ot->idname = "OBJECT_OT_geometry_nodes_modifier_data_block_remove";
ot->exec = geometry_nodes_modifier_data_block_remove_exec;
ot->poll = ED_operator_object_active;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
RNA_def_string(ot->srna, "modifier_name", nullptr, MAX_NAME, "Modifier Name", "");
}
/** \} */

View File

@ -141,6 +141,7 @@ void ED_operatortypes_object()
WM_operatortype_append(OBJECT_OT_skin_armature_create);
WM_operatortype_append(OBJECT_OT_geometry_nodes_input_attribute_toggle);
WM_operatortype_append(OBJECT_OT_geometry_node_tree_copy_assign);
WM_operatortype_append(OBJECT_OT_geometry_nodes_modifier_data_block_remove);
/* grease pencil modifiers */
WM_operatortype_append(OBJECT_OT_gpencil_modifier_add);

View File

@ -2339,6 +2339,31 @@ typedef struct NodesModifierSettings {
struct IDProperty *properties;
} NodesModifierSettings;
/**
* Maps a name (+ optional library name) to a data-block. The name can be stored on disk and is
* remapped to the data-block when the data is loaded.
*
* At run-time, #BakeDataBlockID is used to pair up the data-block and library name.
*/
typedef struct NodesModifierDataBlock {
/**
* Name of the data-block. Can be empty in which case the name of the `id` below is used.
* This only needs to be set manually when the name stored on disk does not exist in the .blend
* file anymore, because e.g. the ID has been renamed.
*/
char *id_name;
/**
* Name of the library the ID is in. Can be empty when the ID is not linked or when `id_name` is
* empty as well and thus the names from the `id` below are used.
*/
char *lib_name;
/** ID that this is mapped to. */
struct ID *id;
/** Type of ID that is referenced by this mapping. */
int id_type;
char _pad[4];
} NodesModifierDataBlock;
typedef struct NodesModifierBake {
/** An id that references a nested node in the node tree. Also see #bNestedNodeRef. */
int id;
@ -2358,6 +2383,17 @@ typedef struct NodesModifierBake {
*/
int frame_start;
int frame_end;
/**
* Maps data-block names to actual data-blocks, so that names stored in caches or on disk can be
* remapped to actual IDs on load. The mapping also makes sure that IDs referenced by baked data
* are not automatically removed because they are not referenced anymore. Furthermore, it allows
* the modifier to add all required IDs to the dependency graph before actually loading the baked
* data.
*/
int data_blocks_num;
int active_data_block;
NodesModifierDataBlock *data_blocks;
} NodesModifierBake;
typedef struct NodesModifierPanel {
@ -2395,6 +2431,7 @@ typedef struct NodesModifierData {
char _pad[3];
int bakes_num;
NodesModifierBake *bakes;
char _pad2[4];
int panels_num;
NodesModifierPanel *panels;

View File

@ -1775,6 +1775,12 @@ static PointerRNA rna_NodesModifierBake_node_get(PointerRNA *ptr)
return RNA_pointer_create(const_cast<ID *>(&tree->id), &RNA_Node, const_cast<bNode *>(node));
}
static StructRNA *rna_NodesModifierBake_data_block_typef(PointerRNA *ptr)
{
NodesModifierDataBlock *data_block = static_cast<NodesModifierDataBlock *>(ptr->data);
return ID_code_to_RNA_type(data_block->id_type);
}
bool rna_GreasePencilModifier_material_poll(PointerRNA *ptr, PointerRNA value)
{
Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
@ -7237,8 +7243,63 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_nodes_data_block(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "NodesModifierDataBlock", nullptr);
RNA_def_struct_sdna(srna, "NodesModifierDataBlock");
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(
prop, "Data-Block Name", "Name that is mapped to the referenced data-block");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "lib_name", PROP_STRING, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop,
"Library Name",
"Used when the data block is not local to the current .blend file but "
"is linked from some library");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "ID");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_pointer_funcs(
prop, nullptr, nullptr, "rna_NodesModifierBake_data_block_typef", nullptr);
RNA_def_property_ui_text(prop, "Data-Block", "");
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
prop = RNA_def_property(srna, "id_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_id_type_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_nodes_bake_data_blocks(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "NodesModifierBakeDataBlocks", nullptr);
RNA_def_struct_sdna(srna, "NodesModifierBake");
RNA_def_struct_ui_text(
srna, "Data-Blocks", "Collection of data-blocks that can be referenced by baked data");
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "active_data_block");
}
static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
{
rna_def_modifier_nodes_bake_data_blocks(brna);
static EnumPropertyItem bake_mode_items[] = {
{NODES_MODIFIER_BAKE_MODE_ANIMATION, "ANIMATION", 0, "Animation", "Bake a frame range"},
{NODES_MODIFIER_BAKE_MODE_STILL, "STILL", 0, "Still", "Bake a single frame"},
@ -7298,6 +7359,11 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
"none in some cases like missing linked data blocks");
RNA_def_property_pointer_funcs(
prop, "rna_NodesModifierBake_node_get", nullptr, nullptr, nullptr);
prop = RNA_def_property(srna, "data_blocks", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "NodesModifierDataBlock");
RNA_def_property_collection_sdna(prop, nullptr, "data_blocks", "data_blocks_num");
RNA_def_property_srna(prop, "NodesModifierBakeDataBlocks");
}
static void rna_def_modifier_nodes_bakes(BlenderRNA *brna)
@ -7338,6 +7404,8 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
rna_def_modifier_nodes_data_block(brna);
rna_def_modifier_nodes_bake(brna);
rna_def_modifier_nodes_bakes(brna);
@ -7385,6 +7453,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
rna_def_modifier_panel_open_prop(srna, "open_manage_panel", 1);
rna_def_modifier_panel_open_prop(srna, "open_bake_panel", 2);
rna_def_modifier_panel_open_prop(srna, "open_named_attributes_panel", 3);
rna_def_modifier_panel_open_prop(srna, "open_bake_data_blocks_panel", 4);
RNA_define_lib_overridable(false);
}

View File

@ -17,6 +17,7 @@ set(INC
../render
../windowmanager
../../../intern/eigen
../../../extern/fmtlib/include
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@ -119,6 +120,7 @@ set(LIB
PRIVATE bf::depsgraph
PUBLIC bf::dna
PRIVATE bf::intern::guardedalloc
extern_fmtlib
)
if(WITH_ALEMBIC)

View File

@ -7,6 +7,7 @@
*/
#include <cstring>
#include <fmt/format.h>
#include <iostream>
#include <sstream>
#include <string>
@ -22,6 +23,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_array_utils.hh"
#include "DNA_collection_types.h"
#include "DNA_curves_types.h"
#include "DNA_defaults.h"
@ -39,6 +41,7 @@
#include "DNA_windowmanager_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_bake_data_block_map.hh"
#include "BKE_bake_geometry_nodes_modifier.hh"
#include "BKE_compute_contexts.hh"
#include "BKE_customdata.hh"
@ -65,6 +68,7 @@
#include "BLT_translation.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include "RNA_access.hh"
@ -73,6 +77,7 @@
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
#include "DEG_depsgraph_writeback_sync.hh"
#include "MOD_modifiertypes.hh"
#include "MOD_nodes.hh"
@ -177,6 +182,14 @@ static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphCont
}
}
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
for (const NodesModifierDataBlock &data_block : Span(bake.data_blocks, bake.data_blocks_num)) {
if (data_block.id) {
used_ids.add(data_block.id);
}
}
}
for (ID *id : used_ids) {
switch ((ID_Type)GS(id->name)) {
case ID_OB: {
@ -270,6 +283,13 @@ static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void
settings->user_data, settings->ob, (ID **)&id_prop->data.pointer, IDWALK_CB_USER);
},
&settings);
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
{
walk(user_data, ob, &data_block.id, IDWALK_CB_USER);
}
}
}
static void foreach_tex_link(ModifierData *md, Object *ob, TexWalkFunc walk, void *user_data)
@ -832,6 +852,53 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md)
}
}
class NodesModifierBakeDataBlockMap : public bake::BakeDataBlockMap {
/** Protects access to everything except `old_mappings` which is read-only during evaluation. */
std::mutex mutex_;
public:
Map<bake::BakeDataBlockID, ID *> old_mappings;
Map<bake::BakeDataBlockID, ID *> new_mappings;
ID *lookup_or_remember_missing(const bake::BakeDataBlockID &key) override
{
if (ID *id = this->old_mappings.lookup_default(key, nullptr)) {
return id;
}
if (this->old_mappings.contains(key)) {
/* Don't allow overwriting old mappings. */
return nullptr;
}
std::lock_guard lock{mutex_};
return this->new_mappings.lookup_or_add(key, nullptr);
}
void try_add(ID &id) override
{
bake::BakeDataBlockID key{id};
if (this->old_mappings.contains(key)) {
return;
}
std::lock_guard lock{mutex_};
this->new_mappings.add_overwrite(std::move(key), &id);
}
private:
ID *lookup_in_map(Map<bake::BakeDataBlockID, ID *> &map,
const bake::BakeDataBlockID &key,
const std::optional<ID_Type> &type)
{
ID *id = map.lookup_default(key, nullptr);
if (!id) {
return nullptr;
}
if (type && GS(id->name) != *type) {
return nullptr;
}
return id;
}
};
namespace sim_input = nodes::sim_input;
namespace sim_output = nodes::sim_output;
@ -917,10 +984,15 @@ static bool try_find_baked_data(bake::NodeBakeCache &bake,
}
class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
private:
public:
static constexpr float max_delta_frames = 1.0f;
mutable Map<int, std::unique_ptr<nodes::SimulationZoneBehavior>> behavior_by_zone_id_;
struct DataPerZone {
nodes::SimulationZoneBehavior behavior;
NodesModifierBakeDataBlockMap data_block_map;
};
mutable Map<int, std::unique_ptr<DataPerZone>> data_by_zone_id_;
const NodesModifierData &nmd_;
const ModifierEvalContext &ctx_;
const Main *bmain_;
@ -1005,26 +1077,38 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
return nullptr;
}
std::lock_guard lock{modifier_cache_->mutex};
return behavior_by_zone_id_
.lookup_or_add_cb(zone_id,
[&]() {
auto info = std::make_unique<nodes::SimulationZoneBehavior>();
this->init_simulation_info(zone_id, *info);
return info;
})
.get();
return &data_by_zone_id_
.lookup_or_add_cb(zone_id,
[&]() {
auto data = std::make_unique<DataPerZone>();
data->behavior.data_block_map = &data->data_block_map;
this->init_simulation_info(
zone_id, data->behavior, data->data_block_map);
return data;
})
->behavior;
}
void init_simulation_info(const int zone_id, nodes::SimulationZoneBehavior &zone_behavior) const
void init_simulation_info(const int zone_id,
nodes::SimulationZoneBehavior &zone_behavior,
NodesModifierBakeDataBlockMap &data_block_map) const
{
bake::SimulationNodeCache &node_cache =
*modifier_cache_->simulation_cache_by_id.lookup_or_add_cb(
zone_id, []() { return std::make_unique<bake::SimulationNodeCache>(); });
const NodesModifierBake &bake = *nmd_.find_bake(zone_id);
const IndexRange sim_frame_range = *bake::get_node_bake_frame_range(
*scene_, *ctx_.object, nmd_, zone_id);
const SubFrame sim_start_frame{int(sim_frame_range.first())};
const SubFrame sim_end_frame{int(sim_frame_range.last())};
if (!node_cache.bake.frames.is_empty()) {
for (const NodesModifierDataBlock &data_block : Span{bake.data_blocks, bake.data_blocks_num})
{
data_block_map.old_mappings.add(data_block, data_block.id);
}
}
/* Try load baked data. */
if (!node_cache.bake.failed_finding_bake) {
if (node_cache.cache_status != bake::CacheStatus::Baked) {
@ -1229,8 +1313,13 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
};
class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
private:
mutable Map<int, std::unique_ptr<nodes::BakeNodeBehavior>> behavior_by_node_id_;
public:
struct DataPerNode {
nodes::BakeNodeBehavior behavior;
NodesModifierBakeDataBlockMap data_block_map;
};
mutable Map<int, std::unique_ptr<DataPerNode>> data_by_node_id_;
const NodesModifierData &nmd_;
const ModifierEvalContext &ctx_;
Main *bmain_;
@ -1255,27 +1344,36 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
return nullptr;
}
std::lock_guard lock{modifier_cache_->mutex};
return behavior_by_node_id_
.lookup_or_add_cb(id,
[&]() {
auto info = std::make_unique<nodes::BakeNodeBehavior>();
this->init_bake_behavior(id, *info);
return info;
})
.get();
return &data_by_node_id_
.lookup_or_add_cb(id,
[&]() {
auto data = std::make_unique<DataPerNode>();
data->behavior.data_block_map = &data->data_block_map;
this->init_bake_behavior(
id, data->behavior, data->data_block_map);
return data;
})
->behavior;
return nullptr;
}
private:
void init_bake_behavior(const int id, nodes::BakeNodeBehavior &behavior) const
void init_bake_behavior(const int id,
nodes::BakeNodeBehavior &behavior,
NodesModifierBakeDataBlockMap &data_block_map) const
{
bake::BakeNodeCache &node_cache = *modifier_cache_->bake_cache_by_id.lookup_or_add_cb(
id, []() { return std::make_unique<bake::BakeNodeCache>(); });
const NodesModifierBake &bake = *nmd_.find_bake(id);
for (const NodesModifierDataBlock &data_block : Span{bake.data_blocks, bake.data_blocks_num}) {
data_block_map.old_mappings.add(data_block, data_block.id);
}
if (depsgraph_is_active_) {
if (modifier_cache_->requested_bakes.contains(id)) {
/* This node is baked during the current evaluation. */
auto &store_info = behavior.emplace<sim_output::StoreNewState>();
auto &store_info = behavior.behavior.emplace<sim_output::StoreNewState>();
store_info.store_fn = [modifier_cache = modifier_cache_,
node_cache = &node_cache,
current_frame = current_frame_](bake::BakeState state) {
@ -1304,7 +1402,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
}
if (node_cache.bake.frames.is_empty()) {
behavior.emplace<sim_output::PassThrough>();
behavior.behavior.emplace<sim_output::PassThrough>();
return;
}
const BakeFrameIndices frame_indices = get_bake_frame_indices(node_cache.bake.frames,
@ -1337,7 +1435,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
if (this->check_read_error(frame_cache, behavior)) {
return;
}
auto &read_single_info = behavior.emplace<sim_output::ReadSingle>();
auto &read_single_info = behavior.behavior.emplace<sim_output::ReadSingle>();
read_single_info.state = frame_cache.state;
}
@ -1355,7 +1453,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
{
return;
}
auto &read_interpolated_info = behavior.emplace<sim_output::ReadInterpolated>();
auto &read_interpolated_info = behavior.behavior.emplace<sim_output::ReadInterpolated>();
read_interpolated_info.mix_factor = (float(current_frame_) - float(prev_frame_cache.frame)) /
(float(next_frame_cache.frame) -
float(prev_frame_cache.frame));
@ -1367,7 +1465,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
nodes::BakeNodeBehavior &behavior) const
{
if (frame_cache.meta_path && frame_cache.state.items_by_id.is_empty()) {
auto &read_error_info = behavior.emplace<sim_output::ReadError>();
auto &read_error_info = behavior.behavior.emplace<sim_output::ReadError>();
read_error_info.message = RPT_("Can not load the baked data");
return true;
}
@ -1375,6 +1473,151 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
}
};
static void add_missing_data_block_mappings(
NodesModifierBake &bake,
const Span<bake::BakeDataBlockID> missing,
FunctionRef<ID *(const bake::BakeDataBlockID &)> get_data_block)
{
const int old_num = bake.data_blocks_num;
const int new_num = old_num + missing.size();
bake.data_blocks = reinterpret_cast<NodesModifierDataBlock *>(
MEM_recallocN(bake.data_blocks, sizeof(NodesModifierDataBlock) * new_num));
for (const int i : missing.index_range()) {
NodesModifierDataBlock &data_block = bake.data_blocks[old_num + i];
const blender::bke::bake::BakeDataBlockID &key = missing[i];
data_block.id_name = BLI_strdup(key.id_name.c_str());
if (!key.lib_name.empty()) {
data_block.lib_name = BLI_strdup(key.lib_name.c_str());
}
data_block.id_type = int(key.type);
ID *id = get_data_block(key);
if (id) {
data_block.id = id;
}
}
bake.data_blocks_num = new_num;
}
static void add_data_block_items_writeback(const ModifierEvalContext &ctx,
NodesModifierData &nmd_eval,
NodesModifierData &nmd_orig,
NodesModifierSimulationParams &simulation_params,
NodesModifierBakeParams &bake_params)
{
Depsgraph *depsgraph = ctx.depsgraph;
Main *bmain = DEG_get_bmain(depsgraph);
Map<int, NodesModifierBakeDataBlockMap *> data_block_maps;
for (auto item : simulation_params.data_by_zone_id_.items()) {
if (bake::SimulationNodeCache *node_cache = nmd_eval.runtime->cache->get_simulation_node_cache(
item.key))
{
/* Only writeback if the bake node has actually baked anything. */
if (!node_cache->bake.frames.is_empty()) {
data_block_maps.add(item.key, &item.value->data_block_map);
}
}
NodesModifierBake &bake = *nmd_eval.find_bake(item.key);
/* The list was reset. */
if (item.value->data_block_map.old_mappings.size() < bake.data_blocks_num) {
deg::sync_writeback::add(
*depsgraph,
[depsgraph = depsgraph,
object_eval = ctx.object,
bmain,
&nmd_orig,
&nmd_eval,
bake_id = item.key]() {
NodesModifierBake &bake_orig = *nmd_orig.find_bake(bake_id);
NodesModifierBake &bake_eval = *nmd_eval.find_bake(bake_id);
dna::array::clear<NodesModifierDataBlock>(&bake_orig.data_blocks,
&bake_orig.data_blocks_num,
&bake_orig.active_data_block,
[](NodesModifierDataBlock *data_block) {
MEM_SAFE_FREE(data_block->id_name);
MEM_SAFE_FREE(data_block->lib_name);
id_us_min(data_block->id);
});
dna::array::clear<NodesModifierDataBlock>(&bake_eval.data_blocks,
&bake_eval.data_blocks_num,
&bake_eval.active_data_block,
[](NodesModifierDataBlock *data_block) {
MEM_SAFE_FREE(data_block->id_name);
MEM_SAFE_FREE(data_block->lib_name);
});
});
}
}
for (auto item : bake_params.data_by_node_id_.items()) {
if (bake::BakeNodeCache *node_cache = nmd_eval.runtime->cache->get_bake_node_cache(item.key)) {
/* Only writeback if the bake node has actually baked anything. */
if (!node_cache->bake.frames.is_empty()) {
data_block_maps.add(item.key, &item.value->data_block_map);
}
}
}
for (auto item : data_block_maps.items()) {
const int bake_id = item.key;
NodesModifierBakeDataBlockMap &data_block_map = *item.value;
if (data_block_map.new_mappings.is_empty()) {
continue;
}
deg::sync_writeback::add(
*depsgraph,
[depsgraph = depsgraph,
object_eval = ctx.object,
bmain,
&nmd_orig,
&nmd_eval,
bake_id,
new_mappings = std::move(data_block_map.new_mappings)]() {
NodesModifierBake &bake_orig = *nmd_orig.find_bake(bake_id);
NodesModifierBake &bake_eval = *nmd_eval.find_bake(bake_id);
Vector<bake::BakeDataBlockID> sorted_new_mappings;
sorted_new_mappings.extend(new_mappings.keys().begin(), new_mappings.keys().end());
bool needs_reevaluation = false;
/* Add new data block mappings to the original modifier. This may do a name lookup in
* bmain to find the data block if there is not faster way to get it. */
add_missing_data_block_mappings(
bake_orig, sorted_new_mappings, [&](const bake::BakeDataBlockID &key) -> ID * {
ID *id_orig = nullptr;
if (ID *id_eval = new_mappings.lookup_default(key, nullptr)) {
id_orig = DEG_get_original_id(id_eval);
}
else {
needs_reevaluation = true;
id_orig = BKE_libblock_find_name_and_library(
bmain, key.id_name.c_str(), key.lib_name.c_str());
}
if (id_orig) {
id_us_plus(id_orig);
}
return id_orig;
});
/* Add new data block mappings to the evaluated modifier. In most cases this makes it so
* the evaluated modifier is in the same state as if it were copied from the updated
* original again. The exception is when a missing data block was found that is not in
* the depsgraph currently. */
add_missing_data_block_mappings(
bake_eval, sorted_new_mappings, [&](const bake::BakeDataBlockID &key) -> ID * {
return new_mappings.lookup_default(key, nullptr);
});
if (needs_reevaluation) {
Object *object_orig = DEG_get_original_object(object_eval);
DEG_id_tag_update(&object_orig->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
}
});
}
}
static void modifyGeometry(ModifierData *md,
const ModifierEvalContext *ctx,
bke::GeometrySet &geometry_set)
@ -1466,6 +1709,10 @@ static void modifyGeometry(ModifierData *md,
nmd_orig->runtime->eval_log = std::move(eval_log);
}
if (DEG_is_active(ctx->depsgraph)) {
add_data_block_items_writeback(*ctx, *nmd, *nmd_orig, simulation_params, bake_params);
}
if (use_orig_index_verts || use_orig_index_edges || use_orig_index_faces) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
/* Add #CD_ORIGINDEX layers if they don't exist already. This is required because the
@ -1923,7 +2170,10 @@ static void draw_output_attributes_panel(const bContext *C,
}
}
static void draw_bake_panel(uiLayout *layout, PointerRNA *modifier_ptr)
static void draw_bake_panel(const bContext *C,
uiLayout *layout,
PointerRNA *modifier_ptr,
NodesModifierData &nmd)
{
uiLayout *col = uiLayoutColumn(layout, false);
uiLayoutSetPropSep(col, true);
@ -2005,7 +2255,7 @@ static void draw_manage_panel(const bContext *C,
if (uiLayout *panel_layout = uiLayoutPanel(
C, layout, IFACE_("Bake"), modifier_ptr, "open_bake_panel"))
{
draw_bake_panel(panel_layout, modifier_ptr);
draw_bake_panel(C, panel_layout, modifier_ptr, nmd);
}
if (uiLayout *panel_layout = uiLayoutPanel(
C, layout, IFACE_("Named Attributes"), modifier_ptr, "open_named_attributes_panel"))
@ -2108,6 +2358,13 @@ static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const Modi
BLO_write_struct_array(writer, NodesModifierBake, nmd->bakes_num, nmd->bakes);
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
BLO_write_string(writer, bake.directory);
BLO_write_struct_array(
writer, NodesModifierDataBlock, bake.data_blocks_num, bake.data_blocks);
for (const NodesModifierDataBlock &item : Span(bake.data_blocks, bake.data_blocks_num)) {
BLO_write_string(writer, item.id_name);
BLO_write_string(writer, item.lib_name);
}
}
BLO_write_struct_array(writer, NodesModifierPanel, nmd->panels_num, nmd->panels);
@ -2141,6 +2398,13 @@ static void blend_read(BlendDataReader *reader, ModifierData *md)
BLO_read_data_address(reader, &nmd->bakes);
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
BLO_read_data_address(reader, &bake.directory);
BLO_read_data_address(reader, &bake.data_blocks);
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
{
BLO_read_data_address(reader, &data_block.id_name);
BLO_read_data_address(reader, &data_block.lib_name);
}
}
BLO_read_data_address(reader, &nmd->panels);
@ -2162,6 +2426,18 @@ static void copy_data(const ModifierData *md, ModifierData *target, const int fl
if (bake.directory) {
bake.directory = BLI_strdup(bake.directory);
}
if (bake.data_blocks) {
bake.data_blocks = static_cast<NodesModifierDataBlock *>(MEM_dupallocN(bake.data_blocks));
for (const int i : IndexRange(bake.data_blocks_num)) {
NodesModifierDataBlock &data_block = bake.data_blocks[i];
if (data_block.id_name) {
data_block.id_name = BLI_strdup(data_block.id_name);
}
if (data_block.lib_name) {
data_block.lib_name = BLI_strdup(data_block.lib_name);
}
}
}
}
}
@ -2198,6 +2474,13 @@ static void free_data(ModifierData *md)
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
MEM_SAFE_FREE(bake.directory);
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
{
MEM_SAFE_FREE(data_block.id_name);
MEM_SAFE_FREE(data_block.lib_name);
}
MEM_SAFE_FREE(bake.data_blocks);
}
MEM_SAFE_FREE(nmd->bakes);

View File

@ -121,6 +121,7 @@ using Behavior = std::variant<PassThrough, StoreNewState, ReadSingle, ReadInterp
struct SimulationZoneBehavior {
sim_input::Behavior input;
sim_output::Behavior output;
bke::bake::BakeDataBlockMap *data_block_map = nullptr;
};
class GeoNodesSimulationParams {
@ -133,8 +134,11 @@ class GeoNodesSimulationParams {
virtual SimulationZoneBehavior *get(const int zone_id) const = 0;
};
/** The set of possible behaviors are the same for both of these nodes currently. */
using BakeNodeBehavior = sim_output::Behavior;
struct BakeNodeBehavior {
/** The set of possible behaviors are the same for both of these nodes currently. */
sim_output::Behavior behavior;
bke::bake::BakeDataBlockMap *data_block_map = nullptr;
};
class GeoNodesBakeParams {
public:

View File

@ -99,18 +99,22 @@ const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type);
const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item);
bke::bake::BakeState move_values_to_simulation_state(
Span<NodeSimulationItem> node_simulation_items, Span<void *> input_values);
Span<NodeSimulationItem> node_simulation_items,
Span<void *> input_values,
bke::bake::BakeDataBlockMap *data_block_map);
void move_simulation_state_to_values(Span<NodeSimulationItem> node_simulation_items,
bke::bake::BakeState zone_state,
const Object &self_object,
const ComputeContext &compute_context,
const bNode &sim_output_node,
bke::bake::BakeDataBlockMap *data_block_map,
Span<void *> r_output_values);
void copy_simulation_state_to_values(Span<NodeSimulationItem> node_simulation_items,
const bke::bake::BakeStateRef &zone_state,
const Object &self_object,
const ComputeContext &compute_context,
const bNode &sim_output_node,
bke::bake::BakeDataBlockMap *data_block_map,
Span<void *> r_output_values);
void copy_with_checked_indices(const GVArray &src,
@ -155,4 +159,6 @@ const EnumPropertyItem *grid_socket_type_items_filter_fn(bContext *C,
void node_geo_exec_with_missing_openvdb(GeoNodeExecParams &params);
void draw_data_blocks(const bContext *C, uiLayout *layout, PointerRNA &bake_rna);
} // namespace blender::nodes

View File

@ -16,6 +16,7 @@
#include "BKE_bake_geometry_nodes_modifier.hh"
#include "BKE_bake_items_socket.hh"
#include "BKE_context.hh"
#include "BKE_screen.hh"
#include "ED_node.hh"
@ -191,24 +192,26 @@ class LazyFunctionForBakeNode final : public LazyFunction {
this->set_default_outputs(params);
return;
}
if (auto *info = std::get_if<sim_output::ReadSingle>(behavior)) {
this->output_cached_state(params, user_data, info->state);
if (auto *info = std::get_if<sim_output::ReadSingle>(&behavior->behavior)) {
this->output_cached_state(params, user_data, behavior->data_block_map, info->state);
}
else if (auto *info = std::get_if<sim_output::ReadInterpolated>(behavior)) {
else if (auto *info = std::get_if<sim_output::ReadInterpolated>(&behavior->behavior)) {
this->output_mixed_cached_state(params,
user_data,
behavior->data_block_map,
*user_data.call_data->self_object(),
*user_data.compute_context,
info->prev_state,
info->next_state,
info->mix_factor);
}
else if (std::get_if<sim_output::PassThrough>(behavior)) {
this->pass_through(params, user_data);
else if (std::get_if<sim_output::PassThrough>(&behavior->behavior)) {
this->pass_through(params, user_data, behavior->data_block_map);
}
else if (auto *info = std::get_if<sim_output::StoreNewState>(behavior)) {
this->store(params, user_data, *info);
else if (auto *info = std::get_if<sim_output::StoreNewState>(&behavior->behavior)) {
this->store(params, user_data, behavior->data_block_map, *info);
}
else if (auto *info = std::get_if<sim_output::ReadError>(behavior)) {
else if (auto *info = std::get_if<sim_output::ReadError>(&behavior->behavior)) {
if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(
user_data))
{
@ -227,9 +230,12 @@ class LazyFunctionForBakeNode final : public LazyFunction {
set_default_remaining_node_outputs(params, node_);
}
void pass_through(lf::Params &params, GeoNodesLFUserData &user_data) const
void pass_through(lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map) const
{
std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(params);
std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(
params, user_data, data_block_map);
if (!bake_state) {
/* Wait for inputs to be computed. */
return;
@ -239,6 +245,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
output_values[i] = params.get_output_data_ptr(i);
}
this->move_bake_state_to_values(std::move(*bake_state),
user_data,
data_block_map,
*user_data.call_data->self_object(),
*user_data.compute_context,
output_values);
@ -249,19 +257,22 @@ class LazyFunctionForBakeNode final : public LazyFunction {
void store(lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const sim_output::StoreNewState &info) const
{
std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(params);
std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(
params, user_data, data_block_map);
if (!bake_state) {
/* Wait for inputs to be computed. */
return;
}
this->output_cached_state(params, user_data, *bake_state);
this->output_cached_state(params, user_data, data_block_map, *bake_state);
info.store_fn(std::move(*bake_state));
}
void output_cached_state(lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const bake::BakeStateRef &bake_state) const
{
Array<void *> output_values(bake_items_.size());
@ -269,6 +280,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
output_values[i] = params.get_output_data_ptr(i);
}
this->copy_bake_state_to_values(bake_state,
user_data,
data_block_map,
*user_data.call_data->self_object(),
*user_data.compute_context,
output_values);
@ -278,6 +291,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
}
void output_mixed_cached_state(lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const Object &self_object,
const ComputeContext &compute_context,
const bake::BakeStateRef &prev_state,
@ -288,7 +303,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
for (const int i : bake_items_.index_range()) {
output_values[i] = params.get_output_data_ptr(i);
}
this->copy_bake_state_to_values(prev_state, self_object, compute_context, output_values);
this->copy_bake_state_to_values(
prev_state, user_data, data_block_map, self_object, compute_context, output_values);
Array<void *> next_values(bake_items_.size());
LinearAllocator<> allocator;
@ -296,7 +312,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
const CPPType &type = *outputs_[i].type;
next_values[i] = allocator.allocate(type.size(), type.alignment());
}
this->copy_bake_state_to_values(next_state, self_object, compute_context, next_values);
this->copy_bake_state_to_values(
next_state, user_data, data_block_map, self_object, compute_context, next_values);
for (const int i : bake_items_.index_range()) {
mix_baked_data_item(eNodeSocketDatatype(bake_items_[i].socket_type),
@ -315,7 +332,10 @@ class LazyFunctionForBakeNode final : public LazyFunction {
}
}
std::optional<bake::BakeState> get_bake_state_from_inputs(lf::Params &params) const
std::optional<bake::BakeState> get_bake_state_from_inputs(
lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map) const
{
Array<void *> input_values(bake_items_.size());
for (const int i : bake_items_.index_range()) {
@ -327,7 +347,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
}
Array<std::unique_ptr<bake::BakeItem>> bake_items = bake::move_socket_values_to_bake_items(
input_values, bake_socket_config_);
input_values, bake_socket_config_, data_block_map);
bake::BakeState bake_state;
for (const int i : bake_items_.index_range()) {
@ -341,6 +361,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
}
void move_bake_state_to_values(bake::BakeState bake_state,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const Object &self_object,
const ComputeContext &compute_context,
Span<void *> r_output_values) const
@ -354,6 +376,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
bake::move_bake_items_to_socket_values(
bake_items,
bake_socket_config_,
data_block_map,
[&](const int i, const CPPType &type) {
return this->make_attribute_field(self_object, compute_context, bake_items_[i], type);
},
@ -361,6 +384,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
}
void copy_bake_state_to_values(const bake::BakeStateRef &bake_state,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const Object &self_object,
const ComputeContext &compute_context,
Span<void *> r_output_values) const
@ -373,6 +398,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
bake::copy_bake_items_to_socket_values(
bake_items,
bake_socket_config_,
data_block_map,
[&](const int i, const CPPType &type) {
return this->make_attribute_field(self_object, compute_context, bake_items_[i], type);
},
@ -591,6 +617,8 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
uiItemR(subcol, &ctx.bake_rna, "frame_end", UI_ITEM_NONE, "End", ICON_NONE);
}
}
draw_data_blocks(C, layout, ctx.bake_rna);
}
static void node_register()
@ -613,6 +641,63 @@ NOD_REGISTER_NODE(node_register)
namespace blender::nodes {
static void draw_bake_data_block_list_item(uiList * /*ui_list*/,
const bContext * /*C*/,
uiLayout *layout,
PointerRNA * /*idataptr*/,
PointerRNA *itemptr,
int /*icon*/,
PointerRNA * /*active_dataptr*/,
const char * /*active_propname*/,
int /*index*/,
int /*flt_flag*/)
{
auto &data_block = *static_cast<NodesModifierDataBlock *>(itemptr->data);
uiLayout *row = uiLayoutRow(layout, true);
std::string name;
if (StringRef(data_block.lib_name).is_empty()) {
name = data_block.id_name;
}
else {
name = fmt::format("{} [{}]", data_block.id_name, data_block.lib_name);
}
uiItemR(row, itemptr, "id", UI_ITEM_NONE, name.c_str(), ICON_NONE);
}
void draw_data_blocks(const bContext *C, uiLayout *layout, PointerRNA &bake_rna)
{
static uiListType *data_block_list = []() {
uiListType *list = MEM_cnew<uiListType>(__func__);
STRNCPY(list->idname, "DATA_UL_nodes_modifier_data_blocks");
list->draw_item = draw_bake_data_block_list_item;
WM_uilisttype_add(list);
return list;
}();
PointerRNA data_blocks_ptr = RNA_pointer_create(
bake_rna.owner_id, &RNA_NodesModifierBakeDataBlocks, bake_rna.data);
{
uiLayout *row = uiLayoutRow(layout, false);
uiTemplateList(row,
C,
data_block_list->idname,
"",
&bake_rna,
"data_blocks",
&data_blocks_ptr,
"active_index",
nullptr,
3,
5,
UILST_LAYOUT_DEFAULT,
0,
UI_TEMPLATE_LIST_FLAG_NONE);
}
}
std::unique_ptr<LazyFunction> get_bake_lazy_function(
const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
{

View File

@ -89,15 +89,17 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
float delta_time = 0.0f;
if (auto *info = std::get_if<sim_input::OutputCopy>(&input_behavior)) {
delta_time = info->delta_time;
this->output_simulation_state_copy(params, user_data, info->state);
this->output_simulation_state_copy(
params, user_data, zone_behavior->data_block_map, info->state);
}
else if (auto *info = std::get_if<sim_input::OutputMove>(&input_behavior)) {
delta_time = info->delta_time;
this->output_simulation_state_move(params, user_data, std::move(info->state));
this->output_simulation_state_move(
params, user_data, zone_behavior->data_block_map, std::move(info->state));
}
else if (std::get_if<sim_input::PassThrough>(&input_behavior)) {
delta_time = 0.0f;
this->pass_through(params, user_data);
this->pass_through(params, user_data, zone_behavior->data_block_map);
}
else {
BLI_assert_unreachable();
@ -114,6 +116,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
void output_simulation_state_copy(lf::Params &params,
const GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const bke::bake::BakeStateRef &zone_state) const
{
Array<void *> outputs(simulation_items_.size());
@ -125,6 +128,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
*user_data.call_data->self_object(),
*user_data.compute_context,
node_,
data_block_map,
outputs);
for (const int i : simulation_items_.index_range()) {
params.output_set(i + 1);
@ -133,6 +137,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
void output_simulation_state_move(lf::Params &params,
const GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
bke::bake::BakeState zone_state) const
{
Array<void *> outputs(simulation_items_.size());
@ -144,13 +149,16 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
*user_data.call_data->self_object(),
*user_data.compute_context,
node_,
data_block_map,
outputs);
for (const int i : simulation_items_.index_range()) {
params.output_set(i + 1);
}
}
void pass_through(lf::Params &params, const GeoNodesLFUserData &user_data) const
void pass_through(lf::Params &params,
const GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map) const
{
Array<void *> input_values(inputs_.size());
for (const int i : inputs_.index_range()) {
@ -163,9 +171,9 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
/* Instead of outputting the initial values directly, convert them to a simulation state and
* then back. This ensures that some geometry processing happens on the data consistently (e.g.
* removing anonymous attributes). */
bke::bake::BakeState bake_state = move_values_to_simulation_state(simulation_items_,
input_values);
this->output_simulation_state_move(params, user_data, std::move(bake_state));
bke::bake::BakeState bake_state = move_values_to_simulation_state(
simulation_items_, input_values, data_block_map);
this->output_simulation_state_move(params, user_data, data_block_map, std::move(bake_state));
}
};

View File

@ -104,6 +104,7 @@ void move_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
const Object &self_object,
const ComputeContext &compute_context,
const bNode &node,
bke::bake::BakeDataBlockMap *data_block_map,
Span<void *> r_output_values)
{
const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
@ -117,6 +118,7 @@ void move_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
bke::bake::move_bake_items_to_socket_values(
bake_items,
config,
data_block_map,
[&](const int i, const CPPType &type) {
return make_attribute_field(
self_object, compute_context, node, node_simulation_items[i], type);
@ -129,6 +131,7 @@ void copy_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
const Object &self_object,
const ComputeContext &compute_context,
const bNode &node,
bke::bake::BakeDataBlockMap *data_block_map,
Span<void *> r_output_values)
{
const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
@ -142,6 +145,7 @@ void copy_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
bke::bake::copy_bake_items_to_socket_values(
bake_items,
config,
data_block_map,
[&](const int i, const CPPType &type) {
return make_attribute_field(
self_object, compute_context, node, node_simulation_items[i], type);
@ -150,12 +154,14 @@ void copy_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
}
bke::bake::BakeState move_values_to_simulation_state(
const Span<NodeSimulationItem> node_simulation_items, const Span<void *> input_values)
const Span<NodeSimulationItem> node_simulation_items,
const Span<void *> input_values,
bke::bake::BakeDataBlockMap *data_block_map)
{
const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
Array<std::unique_ptr<bke::bake::BakeItem>> bake_items =
bke::bake::move_socket_values_to_bake_items(input_values, config);
bke::bake::move_socket_values_to_bake_items(input_values, config, data_block_map);
bke::bake::BakeState bake_state;
for (const int i : node_simulation_items.index_range()) {
@ -525,10 +531,12 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
}
sim_output::Behavior &output_behavior = zone_behavior->output;
if (auto *info = std::get_if<sim_output::ReadSingle>(&output_behavior)) {
this->output_cached_state(params, user_data, info->state);
this->output_cached_state(params, user_data, zone_behavior->data_block_map, info->state);
}
else if (auto *info = std::get_if<sim_output::ReadInterpolated>(&output_behavior)) {
this->output_mixed_cached_state(params,
user_data,
zone_behavior->data_block_map,
*user_data.call_data->self_object(),
*user_data.compute_context,
info->prev_state,
@ -536,10 +544,10 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
info->mix_factor);
}
else if (std::get_if<sim_output::PassThrough>(&output_behavior)) {
this->pass_through(params, user_data);
this->pass_through(params, user_data, zone_behavior->data_block_map);
}
else if (auto *info = std::get_if<sim_output::StoreNewState>(&output_behavior)) {
this->store_new_state(params, user_data, *info);
this->store_new_state(params, user_data, zone_behavior->data_block_map, *info);
}
else {
BLI_assert_unreachable();
@ -553,6 +561,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
void output_cached_state(lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const bke::bake::BakeStateRef &state) const
{
Array<void *> output_values(simulation_items_.size());
@ -564,6 +573,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
*user_data.call_data->self_object(),
*user_data.compute_context,
node_,
data_block_map,
output_values);
for (const int i : simulation_items_.index_range()) {
params.output_set(i);
@ -571,6 +581,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
}
void output_mixed_cached_state(lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const Object &self_object,
const ComputeContext &compute_context,
const bke::bake::BakeStateRef &prev_state,
@ -581,8 +593,13 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
for (const int i : simulation_items_.index_range()) {
output_values[i] = params.get_output_data_ptr(i);
}
copy_simulation_state_to_values(
simulation_items_, prev_state, self_object, compute_context, node_, output_values);
copy_simulation_state_to_values(simulation_items_,
prev_state,
self_object,
compute_context,
node_,
data_block_map,
output_values);
Array<void *> next_values(simulation_items_.size());
LinearAllocator<> allocator;
@ -590,8 +607,13 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
const CPPType &type = *outputs_[i].type;
next_values[i] = allocator.allocate(type.size(), type.alignment());
}
copy_simulation_state_to_values(
simulation_items_, next_state, self_object, compute_context, node_, next_values);
copy_simulation_state_to_values(simulation_items_,
next_state,
self_object,
compute_context,
node_,
data_block_map,
next_values);
for (const int i : simulation_items_.index_range()) {
mix_baked_data_item(eNodeSocketDatatype(simulation_items_[i].socket_type),
@ -610,10 +632,12 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
}
}
void pass_through(lf::Params &params, GeoNodesLFUserData &user_data) const
void pass_through(lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map) const
{
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(params,
true);
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(
params, data_block_map, true);
if (!bake_state) {
/* Wait for inputs to be computed. */
return;
@ -628,6 +652,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
*user_data.call_data->self_object(),
*user_data.compute_context,
node_,
data_block_map,
output_values);
for (const int i : simulation_items_.index_range()) {
params.output_set(i);
@ -636,6 +661,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
void store_new_state(lf::Params &params,
GeoNodesLFUserData &user_data,
bke::bake::BakeDataBlockMap *data_block_map,
const sim_output::StoreNewState &info) const
{
const SocketValueVariant *skip_variant =
@ -649,18 +675,18 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
/* Instead of outputting the values directly, convert them to a bake state and then back. This
* ensures that some geometry processing happens on the data consistently (e.g. removing
* anonymous attributes). */
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(params,
skip);
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(
params, data_block_map, skip);
if (!bake_state) {
/* Wait for inputs to be computed. */
return;
}
this->output_cached_state(params, user_data, *bake_state);
this->output_cached_state(params, user_data, data_block_map, *bake_state);
info.store_fn(std::move(*bake_state));
}
std::optional<bke::bake::BakeState> get_bake_state_from_inputs(lf::Params &params,
const bool skip) const
std::optional<bke::bake::BakeState> get_bake_state_from_inputs(
lf::Params &params, bke::bake::BakeDataBlockMap *data_block_map, const bool skip) const
{
/* Choose which set of input parameters to use. The others are ignored. */
const int params_offset = skip ? skip_inputs_offset_ : solve_inputs_offset_;
@ -673,7 +699,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
return std::nullopt;
}
return move_values_to_simulation_state(simulation_items_, input_values);
return move_values_to_simulation_state(simulation_items_, input_values, data_block_map);
}
};
@ -888,6 +914,8 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
uiItemR(subcol, &bake_rna, "frame_end", UI_ITEM_NONE, "End", ICON_NONE);
}
}
draw_data_blocks(C, layout, bake_rna);
}
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)

View File

@ -965,8 +965,8 @@ class LazyFunctionForBakeInputsUsage : public LazyFunction {
this->set_default_outputs(params);
return;
}
const bool need_inputs = std::holds_alternative<sim_output::PassThrough>(*behavior) ||
std::holds_alternative<sim_output::StoreNewState>(*behavior);
const bool need_inputs = std::holds_alternative<sim_output::PassThrough>(behavior->behavior) ||
std::holds_alternative<sim_output::StoreNewState>(behavior->behavior);
params.set_output(0, need_inputs);
}