For realtime use cases, storing the geometry's state in memory at every frame can be prohibitively expensive. This commit adds an option to disable the caching, stored per object and accessible in the baking panel. The default is still to enable caching. Pull Request: blender/blender#107767
188 lines
4.7 KiB
C++
188 lines
4.7 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
#include "BKE_geometry_set.hh"
|
|
|
|
#include "BLI_map.hh"
|
|
#include "BLI_sub_frame.hh"
|
|
|
|
namespace blender::bke::sim {
|
|
|
|
class BDataSharing;
|
|
class ModifierSimulationCache;
|
|
|
|
class SimulationStateItem {
|
|
public:
|
|
virtual ~SimulationStateItem() = default;
|
|
};
|
|
|
|
class GeometrySimulationStateItem : public SimulationStateItem {
|
|
public:
|
|
GeometrySimulationStateItem(GeometrySet geometry);
|
|
GeometrySet geometry;
|
|
};
|
|
|
|
/**
|
|
* References a field input/output that becomes an attribute as part of the simulation state.
|
|
* The attribute is actually stored in a #GeometrySimulationStateItem, so this just references
|
|
* the attribute's name.
|
|
*/
|
|
class AttributeSimulationStateItem : public SimulationStateItem {
|
|
private:
|
|
std::string name_;
|
|
|
|
public:
|
|
AttributeSimulationStateItem(std::string name) : name_(std::move(name)) {}
|
|
|
|
StringRefNull name() const
|
|
{
|
|
return name_;
|
|
}
|
|
};
|
|
|
|
/** Storage for a single value of a trivial type like `float`, `int`, etc. */
|
|
class PrimitiveSimulationStateItem : public SimulationStateItem {
|
|
private:
|
|
const CPPType &type_;
|
|
void *value_;
|
|
|
|
public:
|
|
PrimitiveSimulationStateItem(const CPPType &type, const void *value);
|
|
~PrimitiveSimulationStateItem();
|
|
|
|
const void *value() const
|
|
{
|
|
return value_;
|
|
}
|
|
|
|
const CPPType &type() const
|
|
{
|
|
return type_;
|
|
}
|
|
};
|
|
|
|
class StringSimulationStateItem : public SimulationStateItem {
|
|
private:
|
|
std::string value_;
|
|
|
|
public:
|
|
StringSimulationStateItem(std::string value);
|
|
|
|
StringRefNull value() const
|
|
{
|
|
return value_;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Storage of values for a single simulation input and output node pair.
|
|
* Used as a cache to allow random access in time, and as an intermediate form before data is
|
|
* baked.
|
|
*/
|
|
class SimulationZoneState {
|
|
public:
|
|
Map<int, std::unique_ptr<SimulationStateItem>> item_by_identifier;
|
|
};
|
|
|
|
/** Identifies a simulation zone (input and output node pair) used by a modifier. */
|
|
struct SimulationZoneID {
|
|
/** Every node identifier in the hierarchy of compute contexts. */
|
|
Vector<int> node_ids;
|
|
|
|
uint64_t hash() const
|
|
{
|
|
return get_default_hash(this->node_ids);
|
|
}
|
|
|
|
friend bool operator==(const SimulationZoneID &a, const SimulationZoneID &b)
|
|
{
|
|
return a.node_ids == b.node_ids;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Stores a single frame of simulation states for all simulation zones in a modifier's node
|
|
* hierarchy.
|
|
*/
|
|
class ModifierSimulationState {
|
|
private:
|
|
mutable bool bake_loaded_;
|
|
|
|
public:
|
|
ModifierSimulationCache *owner_;
|
|
mutable std::mutex mutex_;
|
|
Map<SimulationZoneID, std::unique_ptr<SimulationZoneState>> zone_states_;
|
|
/** File path to folder containing baked meta-data. */
|
|
std::optional<std::string> meta_path_;
|
|
/** File path to folder containing baked data. */
|
|
std::optional<std::string> bdata_dir_;
|
|
|
|
const SimulationZoneState *get_zone_state(const SimulationZoneID &zone_id) const;
|
|
SimulationZoneState &get_zone_state_for_write(const SimulationZoneID &zone_id);
|
|
void ensure_bake_loaded() const;
|
|
};
|
|
|
|
struct ModifierSimulationStateAtFrame {
|
|
SubFrame frame;
|
|
ModifierSimulationState state;
|
|
};
|
|
|
|
enum class CacheState {
|
|
/** The cache is up-to-date with the inputs. */
|
|
Valid,
|
|
/**
|
|
* Nodes or input values have changed since the cache was created, i.e. the output would be
|
|
* different if the simulation was run again.
|
|
*/
|
|
Invalid,
|
|
/** The cache has been baked and will not be invalidated by changing inputs. */
|
|
Baked,
|
|
};
|
|
|
|
struct StatesAroundFrame {
|
|
const ModifierSimulationStateAtFrame *prev = nullptr;
|
|
const ModifierSimulationStateAtFrame *current = nullptr;
|
|
const ModifierSimulationStateAtFrame *next = nullptr;
|
|
};
|
|
|
|
class ModifierSimulationCache {
|
|
private:
|
|
mutable std::mutex states_at_frames_mutex_;
|
|
Vector<std::unique_ptr<ModifierSimulationStateAtFrame>> states_at_frames_;
|
|
/**
|
|
* Used for baking to deduplicate arrays when writing and writing from storage. Sharing info
|
|
* must be kept alive for multiple frames to detect if each data array's version has changed.
|
|
*/
|
|
std::unique_ptr<BDataSharing> bdata_sharing_;
|
|
|
|
friend ModifierSimulationState;
|
|
|
|
public:
|
|
CacheState cache_state_ = CacheState::Valid;
|
|
bool failed_finding_bake_ = false;
|
|
|
|
void try_discover_bake(StringRefNull meta_dir, StringRefNull bdata_dir);
|
|
|
|
bool has_state_at_frame(const SubFrame &frame) const;
|
|
bool has_states() const;
|
|
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;
|
|
|
|
void invalidate()
|
|
{
|
|
cache_state_ = CacheState::Invalid;
|
|
}
|
|
|
|
CacheState cache_state() const
|
|
{
|
|
return cache_state_;
|
|
}
|
|
|
|
void reset();
|
|
void clear_prev_states();
|
|
};
|
|
|
|
} // namespace blender::bke::sim
|