Geometry Nodes: use shared_ptr for sharing simulation cache #108976

Merged
Jacques Lucke merged 1 commits from JacquesLucke/blender:shared-ptr-sim-cache into blender-v3.6-release 2023-06-14 14:04:08 +02:00
6 changed files with 62 additions and 49 deletions

View File

@ -184,4 +184,12 @@ class ModifierSimulationCache {
void clear_prev_states();
};
/**
* Wrap simulation cache in `std::shared_ptr` so that it can be owned by evaluated modifier even if
* the original modifier has been deleted.
*/
struct ModifierSimulationCachePtr {
std::shared_ptr<ModifierSimulationCache> ptr;
};
} // namespace blender::bke::sim

View File

@ -914,9 +914,8 @@ void DepsgraphNodeBuilder::build_object_modifiers(Object *object)
return;
}
if (modifier_node->flag & DEPSOP_FLAG_USER_MODIFIED) {
if (nmd->simulation_cache &&
nmd->simulation_cache->cache_state() == bke::sim::CacheState::Valid) {
nmd->simulation_cache->invalidate();
if (nmd->simulation_cache->ptr->cache_state() == bke::sim::CacheState::Valid) {
nmd->simulation_cache->ptr->invalidate();
}
}
};

View File

@ -89,9 +89,7 @@ static void calculate_simulation_job_startjob(void *customdata,
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->simulation_cache != nullptr) {
nmd->simulation_cache->reset();
}
nmd->simulation_cache->ptr->reset();
}
}
objects_to_calc.append(object);
@ -251,9 +249,7 @@ static void bake_simulation_job_startjob(void *customdata,
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->simulation_cache != nullptr) {
nmd->simulation_cache->reset();
}
nmd->simulation_cache->ptr->reset();
if (StringRef(nmd->simulation_bake_directory).is_empty()) {
nmd->simulation_bake_directory = BLI_strdup(
bke::sim::get_default_modifier_bake_directory(*job.bmain, *object, *md).c_str());
@ -299,7 +295,7 @@ static void bake_simulation_job_startjob(void *customdata,
if (nmd.simulation_cache == nullptr) {
continue;
}
ModifierSimulationCache &sim_cache = *nmd.simulation_cache;
ModifierSimulationCache &sim_cache = *nmd.simulation_cache->ptr;
const ModifierSimulationState *sim_state = sim_cache.get_state_at_exact_frame(frame);
if (sim_state == nullptr || sim_state->zone_states_.is_empty()) {
continue;
@ -343,7 +339,7 @@ static void bake_simulation_job_startjob(void *customdata,
NodesModifierData &nmd = *modifier_bake_data.nmd;
if (nmd.simulation_cache) {
/* Tag the caches as being baked so that they are not changed anymore. */
nmd.simulation_cache->cache_state_ = CacheState::Baked;
nmd.simulation_cache->ptr->cache_state_ = CacheState::Baked;
}
}
DEG_id_tag_update(&object_bake_data.object->id, ID_RECALC_GEOMETRY);
@ -441,9 +437,7 @@ static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->simulation_cache != nullptr) {
nmd->simulation_cache->reset();
}
nmd->simulation_cache->ptr->reset();
if (StringRef(nmd->simulation_bake_directory).is_empty()) {
continue;
}

View File

@ -774,7 +774,7 @@ void timeline_draw_cache(const SpaceAction *saction, const Object *ob, const Sce
continue;
}
timeline_cache_draw_simulation_nodes(
*scene, *nmd->simulation_cache, y_offset, cache_draw_height, pos_id);
*scene, *nmd->simulation_cache->ptr, y_offset, cache_draw_height, pos_id);
y_offset += cache_draw_height;
}
}

View File

@ -12,11 +12,11 @@
#ifdef __cplusplus
namespace blender::bke::sim {
class ModifierSimulationCache;
class ModifierSimulationCachePtr;
}
using ModifierSimulationCacheHandle = blender::bke::sim::ModifierSimulationCache;
using ModifierSimulationCachePtrHandle = blender::bke::sim::ModifierSimulationCachePtr;
#else
typedef struct ModifierSimulationCacheHandle ModifierSimulationCacheHandle;
typedef struct ModifierSimulationCachePtrHandle ModifierSimulationCachePtrHandle;
#endif
#ifdef __cplusplus
@ -2331,7 +2331,12 @@ typedef struct NodesModifierData {
*/
void *runtime_eval_log;
ModifierSimulationCacheHandle *simulation_cache;
/**
* Simulation cache that is shared between original and evaluated modifiers. This allows the
* original modifier to be removed, without also removing the simulation state which may still be
* used by the evaluated modifier.
*/
ModifierSimulationCachePtrHandle *simulation_cache;
} NodesModifierData;
typedef struct MeshToVolumeModifierData {

View File

@ -1132,7 +1132,6 @@ static void store_output_attributes(GeometrySet &geometry,
}
static void prepare_simulation_states_for_evaluation(const NodesModifierData &nmd,
NodesModifierData &nmd_orig,
const ModifierEvalContext &ctx,
nodes::GeoNodesModifierData &exec_data)
{
@ -1142,23 +1141,21 @@ static void prepare_simulation_states_for_evaluation(const NodesModifierData &nm
const SubFrame start_frame = scene->r.sfra;
const bool is_start_frame = current_frame == start_frame;
if (DEG_is_active(ctx.depsgraph)) {
if (nmd_orig.simulation_cache == nullptr) {
nmd_orig.simulation_cache = MEM_new<bke::sim::ModifierSimulationCache>(__func__);
}
/* This cache may be shared between original and evaluated modifiers. */
blender::bke::sim::ModifierSimulationCache &simulation_cache = *nmd.simulation_cache->ptr;
if (DEG_is_active(ctx.depsgraph)) {
{
/* Try to use baked data. */
const StringRefNull bmain_path = BKE_main_blendfile_path(bmain);
if (nmd_orig.simulation_cache->cache_state() != bke::sim::CacheState::Baked &&
!bmain_path.is_empty())
if (simulation_cache.cache_state() != bke::sim::CacheState::Baked && !bmain_path.is_empty())
{
if (!StringRef(nmd.simulation_bake_directory).is_empty()) {
if (const char *base_path = ID_BLEND_PATH(bmain, &ctx.object->id)) {
char absolute_bake_dir[FILE_MAX];
STRNCPY(absolute_bake_dir, nmd.simulation_bake_directory);
BLI_path_abs(absolute_bake_dir, base_path);
nmd_orig.simulation_cache->try_discover_bake(absolute_bake_dir);
simulation_cache.try_discover_bake(absolute_bake_dir);
}
}
}
@ -1166,30 +1163,30 @@ static void prepare_simulation_states_for_evaluation(const NodesModifierData &nm
{
/* Reset cached data if necessary. */
const bke::sim::StatesAroundFrame sim_states =
nmd_orig.simulation_cache->get_states_around_frame(current_frame);
if (nmd_orig.simulation_cache->cache_state() == bke::sim::CacheState::Invalid &&
const bke::sim::StatesAroundFrame sim_states = simulation_cache.get_states_around_frame(
current_frame);
if (simulation_cache.cache_state() == bke::sim::CacheState::Invalid &&
(current_frame == start_frame ||
(sim_states.current == nullptr && sim_states.prev == nullptr &&
sim_states.next != nullptr)))
{
nmd_orig.simulation_cache->reset();
simulation_cache.reset();
}
}
/* Decide if a new simulation state should be created in this evaluation. */
const bke::sim::StatesAroundFrame sim_states =
nmd_orig.simulation_cache->get_states_around_frame(current_frame);
if (nmd_orig.simulation_cache->cache_state() != bke::sim::CacheState::Baked) {
const bke::sim::StatesAroundFrame sim_states = simulation_cache.get_states_around_frame(
current_frame);
if (simulation_cache.cache_state() != bke::sim::CacheState::Baked) {
if (sim_states.current == nullptr) {
if (is_start_frame || !nmd_orig.simulation_cache->has_states()) {
if (is_start_frame || !simulation_cache.has_states()) {
bke::sim::ModifierSimulationState &current_sim_state =
nmd_orig.simulation_cache->get_state_at_frame_for_write(current_frame);
simulation_cache.get_state_at_frame_for_write(current_frame);
exec_data.current_simulation_state_for_write = &current_sim_state;
exec_data.simulation_time_delta = 0.0f;
if (!is_start_frame) {
/* When starting a new simulation at another frame than the start frame, it can't match
* what would be baked, so invalidate it immediately. */
nmd_orig.simulation_cache->invalidate();
simulation_cache.invalidate();
}
}
else if (sim_states.prev != nullptr && sim_states.next == nullptr) {
@ -1197,10 +1194,10 @@ static void prepare_simulation_states_for_evaluation(const NodesModifierData &nm
const float scene_delta_frames = float(current_frame) - float(sim_states.prev->frame);
const float delta_frames = std::min(max_delta_frames, scene_delta_frames);
if (delta_frames != scene_delta_frames) {
nmd_orig.simulation_cache->invalidate();
simulation_cache.invalidate();
}
bke::sim::ModifierSimulationState &current_sim_state =
nmd_orig.simulation_cache->get_state_at_frame_for_write(current_frame);
simulation_cache.get_state_at_frame_for_write(current_frame);
exec_data.current_simulation_state_for_write = &current_sim_state;
const float delta_seconds = delta_frames / FPS;
exec_data.simulation_time_delta = delta_seconds;
@ -1209,13 +1206,9 @@ static void prepare_simulation_states_for_evaluation(const NodesModifierData &nm
}
}
if (nmd_orig.simulation_cache == nullptr) {
return;
}
/* Load read-only states to give nodes access to cached data. */
const bke::sim::StatesAroundFrame sim_states =
nmd_orig.simulation_cache->get_states_around_frame(current_frame);
const bke::sim::StatesAroundFrame sim_states = simulation_cache.get_states_around_frame(
current_frame);
if (sim_states.current) {
sim_states.current->state.ensure_bake_loaded();
exec_data.current_simulation_state = &sim_states.current->state;
@ -1271,7 +1264,7 @@ static GeometrySet compute_geometry(const bNodeTree &btree,
geo_nodes_modifier_data.self_object = ctx->object;
auto eval_log = std::make_unique<geo_log::GeoModifierLog>();
prepare_simulation_states_for_evaluation(*nmd, *nmd_orig, *ctx, geo_nodes_modifier_data);
prepare_simulation_states_for_evaluation(*nmd, *ctx, geo_nodes_modifier_data);
Set<ComputeContextHash> socket_log_contexts;
if (logging_enabled(ctx)) {
@ -1361,7 +1354,7 @@ static GeometrySet compute_geometry(const bNodeTree &btree,
/* When caching is turned off, remove all states except the last which was just created in this
* evaluation. Check if active status to avoid changing original data in other depsgraphs. */
if (!(ctx->object->flag & OB_FLAG_USE_SIMULATION_CACHE)) {
nmd_orig->simulation_cache->clear_prev_states();
nmd_orig->simulation_cache->ptr->clear_prev_states();
}
}
@ -2040,6 +2033,13 @@ static void blendWrite(BlendWriter *writer, const ID * /*id_owner*/, const Modif
}
}
static blender::bke::sim::ModifierSimulationCachePtr *new_simulation_cache()
{
auto *simulation_cache = MEM_new<blender::bke::sim::ModifierSimulationCachePtr>(__func__);
simulation_cache->ptr = std::make_shared<blender::bke::sim::ModifierSimulationCache>();
return simulation_cache;
}
static void blendRead(BlendDataReader *reader, ModifierData *md)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
@ -2052,7 +2052,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
IDP_BlendDataRead(reader, &nmd->settings.properties);
}
nmd->runtime_eval_log = nullptr;
nmd->simulation_cache = nullptr;
nmd->simulation_cache = new_simulation_cache();
}
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
@ -2063,7 +2063,14 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
BKE_modifier_copydata_generic(md, target, flag);
tnmd->runtime_eval_log = nullptr;
tnmd->simulation_cache = nullptr;
if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
/* Share the simulation cache between the original and evaluated modifier. */
tnmd->simulation_cache = MEM_new<blender::bke::sim::ModifierSimulationCachePtr>(
__func__, *nmd->simulation_cache);
}
else {
tnmd->simulation_cache = new_simulation_cache();
}
tnmd->simulation_bake_directory = nmd->simulation_bake_directory ?
BLI_strdup(nmd->simulation_bake_directory) :
nullptr;