Geometry Nodes: Make simulation caching optional #107767

Merged
Hans Goudey merged 13 commits from HooglyBoogly/blender:simulation-use-cache-option into main 2023-05-10 16:01:44 +02:00
5 changed files with 15 additions and 124 deletions
Showing only changes of commit 5d8e18d6e6 - Show all commits

View File

@ -120,7 +120,6 @@ class ModifierSimulationState {
const SimulationZoneState *get_zone_state(const SimulationZoneID &zone_id) const;
SimulationZoneState &get_zone_state_for_write(const SimulationZoneID &zone_id);
void remove_zone_state(const SimulationZoneID &zone_id);
void ensure_bake_loaded() const;
};
@ -147,12 +146,6 @@ struct StatesAroundFrame {
const ModifierSimulationStateAtFrame *next = nullptr;
};
struct MutableStatesAroundFrame {
ModifierSimulationStateAtFrame *prev = nullptr;
ModifierSimulationStateAtFrame *current = nullptr;
ModifierSimulationStateAtFrame *next = nullptr;
};
class ModifierSimulationCache {
private:

Having this struct seems a little wrong because we should never really need write access to all of these simulation states at the same time afaik.

Having this struct seems a little wrong because we should never really need write access to all of these simulation states at the same time afaik.
mutable std::mutex states_at_frames_mutex_;
@ -176,7 +169,6 @@ class ModifierSimulationCache {
const ModifierSimulationState *get_state_at_exact_frame(const SubFrame &frame) const;
ModifierSimulationState &get_state_at_frame_for_write(const SubFrame &frame);
StatesAroundFrame get_states_around_frame(const SubFrame &frame) const;
MutableStatesAroundFrame get_states_around_frame_for_write(const SubFrame &frame);
void invalidate()
{
@ -189,6 +181,7 @@ class ModifierSimulationCache {
}
void reset();
void clear_prev_states();
};
} // namespace blender::bke::sim

View File

@ -158,15 +158,6 @@ StatesAroundFrame ModifierSimulationCache::get_states_around_frame(const SubFram
return states_around_frame;
}
MutableStatesAroundFrame ModifierSimulationCache::get_states_around_frame_for_write(
const SubFrame &frame)
{
StatesAroundFrame states = this->get_states_around_frame(frame);
return {const_cast<ModifierSimulationStateAtFrame *>(states.prev),
const_cast<ModifierSimulationStateAtFrame *>(states.current),
const_cast<ModifierSimulationStateAtFrame *>(states.next)};
}
const SimulationZoneState *ModifierSimulationState::get_zone_state(
const SimulationZoneID &zone_id) const
{
@ -185,12 +176,6 @@ SimulationZoneState &ModifierSimulationState::get_zone_state_for_write(
[]() { return std::make_unique<SimulationZoneState>(); });
}
void ModifierSimulationState::remove_zone_state(const SimulationZoneID &zone_id)
{
std::lock_guard lock{mutex_};
zone_states_.remove(zone_id);
}
void ModifierSimulationState::ensure_bake_loaded() const
{
std::scoped_lock lock{mutex_};
@ -219,6 +204,15 @@ void ModifierSimulationState::ensure_bake_loaded() const
bake_loaded_ = true;
}
void ModifierSimulationCache::clear_prev_states()
{
std::lock_guard lock(states_at_frames_mutex_);
std::unique_ptr<ModifierSimulationStateAtFrame> temp = std::move(states_at_frames_.last());
states_at_frames_.clear_and_shrink();
bdata_sharing_.reset();
states_at_frames_.append(std::move(temp));
}
void ModifierSimulationCache::reset()
{
std::lock_guard lock(states_at_frames_mutex_);

View File

@ -1130,104 +1130,6 @@ static void store_output_attributes(GeometrySet &geometry,
store_computed_output_attributes(geometry, attributes_to_store);
}
static void prepare_simulation_states_for_evaluation_no_cache(
const NodesModifierData &nmd,
NodesModifierData &nmd_orig,
const ModifierEvalContext &ctx,
nodes::GeoNodesModifierData &exec_data)
{
const Main *bmain = DEG_get_bmain(ctx.depsgraph);
const SubFrame current_frame = DEG_get_ctime(ctx.depsgraph);
const Scene *scene = DEG_get_input_scene(ctx.depsgraph);
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__);
}
{
/* 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())
{
nmd_orig.simulation_cache->try_discover_bake(
bke::sim::get_meta_directory(*bmain, *ctx.object, nmd.modifier),
bke::sim::get_bdata_directory(*bmain, *ctx.object, nmd.modifier));
}
}
{
/* 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 &&
(current_frame == start_frame ||
(sim_states.current == nullptr && sim_states.prev == nullptr &&
sim_states.next != nullptr)))
{
nmd_orig.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) {
if (sim_states.current == nullptr) {
if (is_start_frame || !nmd_orig.simulation_cache->has_states()) {
bke::sim::ModifierSimulationState &current_sim_state =
nmd_orig.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();
}
}
else if (sim_states.prev != nullptr && sim_states.next == nullptr) {
const float max_delta_frames = 1.0f;
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();
}
bke::sim::ModifierSimulationState &current_sim_state =
nmd_orig.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;
}
}
}
}
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);
if (sim_states.current) {
sim_states.current->state.ensure_bake_loaded();
exec_data.current_simulation_state = &sim_states.current->state;
}
if (sim_states.prev) {
sim_states.prev->state.ensure_bake_loaded();
exec_data.prev_simulation_state = &sim_states.prev->state;
if (sim_states.next) {
sim_states.next->state.ensure_bake_loaded();
exec_data.next_simulation_state = &sim_states.next->state;
exec_data.simulation_state_mix_factor =
(float(current_frame) - float(sim_states.prev->frame)) /
(float(sim_states.next->frame) - float(sim_states.prev->frame));
}
}
}
static void prepare_simulation_states_for_evaluation(const NodesModifierData &nmd,
NodesModifierData &nmd_orig,
const ModifierEvalContext &ctx,
@ -1449,6 +1351,10 @@ static GeometrySet compute_geometry(const bNodeTree &btree,
nmd_orig->runtime_eval_log = eval_log.release();
}
if (!(ctx->object->flag & OB_FLAG_USE_SIMULATION_CACHE)) {
nmd_orig->simulation_cache->clear_prev_states();

Should only be done when the depsgraph is active. Other depsgraph should generally never modify the original data.

Should only be done when the depsgraph is active. Other depsgraph should generally never modify the original data.
}
return output_geometry_set;
}

View File

@ -48,9 +48,8 @@ struct GeoNodesModifierData {
geo_eval_log::GeoModifierLog *eval_log = nullptr;
/** Read-only simulation states around the current frame. */
bke::sim::ModifierSimulationCache *simulation_cache = nullptr;
const bke::sim::ModifierSimulationState *current_simulation_state = nullptr;
bke::sim::ModifierSimulationState *prev_simulation_state = nullptr;
const bke::sim::ModifierSimulationState *prev_simulation_state = nullptr;
const bke::sim::ModifierSimulationState *next_simulation_state = nullptr;
float simulation_state_mix_factor = 0.0f;
/** Used when the evaluation should create a new simulation state. */

View File

@ -107,7 +107,6 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
/* Wait until all inputs are available. */
return;
}
for (const int i : input_values.index_range()) {
inputs_[i].type->move_construct(input_values[i], output_values[i]);
}