diff --git a/source/blender/blenkernel/BKE_simulation_state.hh b/source/blender/blenkernel/BKE_simulation_state.hh index 41e698af809..e8630999ceb 100644 --- a/source/blender/blenkernel/BKE_simulation_state.hh +++ b/source/blender/blenkernel/BKE_simulation_state.hh @@ -149,6 +149,9 @@ struct StatesAroundFrame { class ModifierSimulationCache { private: mutable std::mutex states_at_frames_mutex_; + /** + * All simulation states, sorted by frame. + */ Vector> states_at_frames_; /** * Used for baking to deduplicate arrays when writing and writing from storage. Sharing info diff --git a/source/blender/blenkernel/intern/simulation_state.cc b/source/blender/blenkernel/intern/simulation_state.cc index ef912a89ea7..f93cbe0105b 100644 --- a/source/blender/blenkernel/intern/simulation_state.cc +++ b/source/blender/blenkernel/intern/simulation_state.cc @@ -9,6 +9,7 @@ #include "DNA_node_types.h" #include "DNA_pointcloud_types.h" +#include "BLI_binary_search.hh" #include "BLI_fileops.hh" #include "BLI_hash_md5.h" #include "BLI_path_util.h" @@ -92,15 +93,34 @@ void ModifierSimulationCache::try_discover_bake(const StringRefNull absolute_bak } } +static int64_t find_state_at_frame( + const Span> states, const SubFrame &frame) +{ + const int64_t i = binary_search::find_predicate_begin( + states, [&](const auto &item) { return item->frame >= frame; }); + if (i == states.size()) { + return -1; + } + return i; +} + +static int64_t find_state_at_frame_exact( + const Span> states, const SubFrame &frame) +{ + const int64_t i = find_state_at_frame(states, frame); + if (i == -1) { + return -1; + } + if (states[i]->frame != frame) { + return -1; + } + return i; +} + bool ModifierSimulationCache::has_state_at_frame(const SubFrame &frame) const { std::lock_guard lock(states_at_frames_mutex_); - for (const auto &item : states_at_frames_) { - if (item->frame == frame) { - return true; - } - } - return false; + return find_state_at_frame_exact(states_at_frames_, frame) != -1; } bool ModifierSimulationCache::has_states() const @@ -113,23 +133,26 @@ const ModifierSimulationState *ModifierSimulationCache::get_state_at_exact_frame const SubFrame &frame) const { std::lock_guard lock(states_at_frames_mutex_); - for (const auto &item : states_at_frames_) { - if (item->frame == frame) { - return &item->state; - } + const int64_t i = find_state_at_frame_exact(states_at_frames_, frame); + if (i == -1) { + return nullptr; } - return nullptr; + return &states_at_frames_[i]->state; } ModifierSimulationState &ModifierSimulationCache::get_state_at_frame_for_write( const SubFrame &frame) { std::lock_guard lock(states_at_frames_mutex_); - for (const auto &item : states_at_frames_) { - if (item->frame == frame) { - return item->state; - } + const int64_t i = find_state_at_frame_exact(states_at_frames_, frame); + if (i != -1) { + return states_at_frames_[i]->state; } + + if (!states_at_frames_.is_empty()) { + BLI_assert(frame > states_at_frames_.last()->frame); + } + states_at_frames_.append(std::make_unique()); states_at_frames_.last()->frame = frame; states_at_frames_.last()->state.owner_ = this; @@ -139,23 +162,22 @@ ModifierSimulationState &ModifierSimulationCache::get_state_at_frame_for_write( StatesAroundFrame ModifierSimulationCache::get_states_around_frame(const SubFrame &frame) const { std::lock_guard lock(states_at_frames_mutex_); - StatesAroundFrame states_around_frame; - for (const auto &item : states_at_frames_) { - if (item->frame < frame) { - if (states_around_frame.prev == nullptr || item->frame > states_around_frame.prev->frame) { - states_around_frame.prev = item.get(); - } - } - if (item->frame == frame) { - if (states_around_frame.current == nullptr) { - states_around_frame.current = item.get(); - } - } - if (item->frame > frame) { - if (states_around_frame.next == nullptr || item->frame < states_around_frame.next->frame) { - states_around_frame.next = item.get(); - } + const int64_t i = find_state_at_frame(states_at_frames_, frame); + StatesAroundFrame states_around_frame{}; + if (i == -1) { + if (!states_at_frames_.is_empty() && states_at_frames_.last()->frame < frame) { + states_around_frame.prev = states_at_frames_.last().get(); } + return states_around_frame; + } + if (states_at_frames_[i]->frame == frame) { + states_around_frame.current = states_at_frames_[i].get(); + } + if (i > 0) { + states_around_frame.prev = states_at_frames_[i - 1].get(); + } + if (i < states_at_frames_.size() - 2) { + states_around_frame.next = states_at_frames_[i + 1].get(); } return states_around_frame; }