Sometimes the modifier has never been evaluated by an active depsgraph before it is used for rendering. In this case, the baked data was never loaded. Now also allow checking for baked data in a non-active depsgraph. The locking that is in-place already should probably already be enough to make this thread-safe but maybe that could be made more explicit.
227 lines
6.4 KiB
C++
227 lines
6.4 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "BKE_curves.hh"
|
|
#include "BKE_simulation_state.hh"
|
|
#include "BKE_simulation_state_serialize.hh"
|
|
|
|
#include "DNA_curves_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_node_types.h"
|
|
#include "DNA_pointcloud_types.h"
|
|
|
|
#include "BLI_fileops.hh"
|
|
#include "BLI_hash_md5.h"
|
|
#include "BLI_path_util.h"
|
|
|
|
namespace blender::bke::sim {
|
|
|
|
GeometrySimulationStateItem::GeometrySimulationStateItem(GeometrySet geometry)
|
|
: geometry(std::move(geometry))
|
|
{
|
|
}
|
|
|
|
PrimitiveSimulationStateItem::PrimitiveSimulationStateItem(const CPPType &type, const void *value)
|
|
: type_(type)
|
|
{
|
|
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
|
|
type.copy_construct(value, value_);
|
|
}
|
|
|
|
PrimitiveSimulationStateItem::~PrimitiveSimulationStateItem()
|
|
{
|
|
type_.destruct(value_);
|
|
MEM_freeN(value_);
|
|
}
|
|
|
|
StringSimulationStateItem::StringSimulationStateItem(std::string value) : value_(std::move(value))
|
|
{
|
|
}
|
|
|
|
void ModifierSimulationCache::try_discover_bake(const StringRefNull absolute_bake_dir)
|
|
{
|
|
if (failed_finding_bake_) {
|
|
return;
|
|
}
|
|
|
|
char meta_dir[FILE_MAX];
|
|
BLI_path_join(meta_dir, sizeof(meta_dir), absolute_bake_dir.c_str(), "meta");
|
|
char bdata_dir[FILE_MAX];
|
|
BLI_path_join(bdata_dir, sizeof(bdata_dir), absolute_bake_dir.c_str(), "bdata");
|
|
|
|
if (!BLI_is_dir(meta_dir) || !BLI_is_dir(bdata_dir)) {
|
|
failed_finding_bake_ = true;
|
|
return;
|
|
}
|
|
|
|
direntry *dir_entries = nullptr;
|
|
const int dir_entries_num = BLI_filelist_dir_contents(meta_dir, &dir_entries);
|
|
BLI_SCOPED_DEFER([&]() { BLI_filelist_free(dir_entries, dir_entries_num); });
|
|
|
|
if (dir_entries_num == 0) {
|
|
failed_finding_bake_ = true;
|
|
return;
|
|
}
|
|
|
|
this->reset();
|
|
|
|
{
|
|
std::lock_guard lock(states_at_frames_mutex_);
|
|
|
|
for (const int i : IndexRange(dir_entries_num)) {
|
|
const direntry &dir_entry = dir_entries[i];
|
|
const StringRefNull dir_entry_path = dir_entry.path;
|
|
if (!dir_entry_path.endswith(".json")) {
|
|
continue;
|
|
}
|
|
char modified_file_name[FILE_MAX];
|
|
STRNCPY(modified_file_name, dir_entry.relname);
|
|
BLI_str_replace_char(modified_file_name, '_', '.');
|
|
|
|
const SubFrame frame = std::stof(modified_file_name);
|
|
|
|
auto new_state_at_frame = std::make_unique<ModifierSimulationStateAtFrame>();
|
|
new_state_at_frame->frame = frame;
|
|
new_state_at_frame->state.bdata_dir_ = bdata_dir;
|
|
new_state_at_frame->state.meta_path_ = dir_entry.path;
|
|
new_state_at_frame->state.owner_ = this;
|
|
states_at_frames_.append(std::move(new_state_at_frame));
|
|
}
|
|
|
|
bdata_sharing_ = std::make_unique<BDataSharing>();
|
|
cache_state_ = CacheState::Baked;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool ModifierSimulationCache::has_states() const
|
|
{
|
|
std::lock_guard lock(states_at_frames_mutex_);
|
|
return !states_at_frames_.is_empty();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
states_at_frames_.append(std::make_unique<ModifierSimulationStateAtFrame>());
|
|
states_at_frames_.last()->frame = frame;
|
|
states_at_frames_.last()->state.owner_ = this;
|
|
return states_at_frames_.last()->state;
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
return states_around_frame;
|
|
}
|
|
|
|
const SimulationZoneState *ModifierSimulationState::get_zone_state(
|
|
const SimulationZoneID &zone_id) const
|
|
{
|
|
std::lock_guard lock{mutex_};
|
|
if (auto *ptr = zone_states_.lookup_ptr(zone_id)) {
|
|
return ptr->get();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
SimulationZoneState &ModifierSimulationState::get_zone_state_for_write(
|
|
const SimulationZoneID &zone_id)
|
|
{
|
|
std::lock_guard lock{mutex_};
|
|
return *zone_states_.lookup_or_add_cb(zone_id,
|
|
[]() { return std::make_unique<SimulationZoneState>(); });
|
|
}
|
|
|
|
void ModifierSimulationState::ensure_bake_loaded() const
|
|
{
|
|
std::scoped_lock lock{mutex_};
|
|
if (bake_loaded_) {
|
|
return;
|
|
}
|
|
if (!meta_path_ || !bdata_dir_) {
|
|
return;
|
|
}
|
|
|
|
const std::shared_ptr<io::serialize::Value> io_root_value = io::serialize::read_json_file(
|
|
*meta_path_);
|
|
if (!io_root_value) {
|
|
return;
|
|
}
|
|
const DictionaryValue *io_root = io_root_value->as_dictionary_value();
|
|
if (!io_root) {
|
|
return;
|
|
}
|
|
|
|
const DiskBDataReader bdata_reader{*bdata_dir_};
|
|
deserialize_modifier_simulation_state(*io_root,
|
|
bdata_reader,
|
|
*owner_->bdata_sharing_,
|
|
const_cast<ModifierSimulationState &>(*this));
|
|
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_);
|
|
states_at_frames_.clear();
|
|
bdata_sharing_.reset();
|
|
cache_state_ = CacheState::Valid;
|
|
}
|
|
|
|
} // namespace blender::bke::sim
|