Geometry Nodes: Avoid geometry copies in "no cache" simulation
When we don't need to preserve a persistent cache, we can use the geometry from the last frame directly rather than copying it. Though implicit lets us avoid copying large data arrays when they aren't changed, this can still give a large improvement for something like particle simulation where the majority of the data was copied every frame. Pull Request: https://projects.blender.org/blender/blender/pulls/109742
This commit is contained in:
@@ -122,6 +122,7 @@ class ModifierSimulationState {
|
||||
/** File path to folder containing baked data. */
|
||||
std::optional<std::string> bdata_dir_;
|
||||
|
||||
SimulationZoneState *get_zone_state(const SimulationZoneID &zone_id);
|
||||
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 bNodeTree &ntree) const;
|
||||
|
||||
@@ -185,6 +185,15 @@ StatesAroundFrame ModifierSimulationCache::get_states_around_frame(const SubFram
|
||||
return states_around_frame;
|
||||
}
|
||||
|
||||
SimulationZoneState *ModifierSimulationState::get_zone_state(const SimulationZoneID &zone_id)
|
||||
{
|
||||
std::lock_guard lock{mutex_};
|
||||
if (auto *ptr = zone_states_.lookup_ptr(zone_id)) {
|
||||
return ptr->get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SimulationZoneState *ModifierSimulationState::get_zone_state(
|
||||
const SimulationZoneID &zone_id) const
|
||||
{
|
||||
|
||||
@@ -751,7 +751,7 @@ static void prepare_simulation_states_for_evaluation(const NodesModifierData &nm
|
||||
realtime_cache.prev_frame = realtime_cache.current_frame;
|
||||
realtime_cache.prev_state = std::move(realtime_cache.current_state);
|
||||
if (realtime_cache.prev_state) {
|
||||
exec_data.prev_simulation_state = realtime_cache.prev_state.get();
|
||||
exec_data.prev_simulation_state_mutable = realtime_cache.prev_state.get();
|
||||
}
|
||||
|
||||
/* Create a new current state used to pass the data to the next frame. */
|
||||
|
||||
@@ -59,6 +59,12 @@ struct GeoNodesModifierData {
|
||||
bke::sim::ModifierSimulationState *current_simulation_state_for_write = nullptr;
|
||||
float simulation_time_delta = 0.0f;
|
||||
|
||||
/**
|
||||
* The same as #prev_simulation_state, but the cached values can be moved from,
|
||||
* to keep data managed by implicit sharing mutable.
|
||||
*/
|
||||
bke::sim::ModifierSimulationState *prev_simulation_state_mutable = nullptr;
|
||||
|
||||
/**
|
||||
* Some nodes should be executed even when their output is not used (e.g. active viewer nodes and
|
||||
* the node groups they are contained in).
|
||||
|
||||
@@ -139,15 +139,21 @@ void socket_declarations_for_simulation_items(Span<NodeSimulationItem> items,
|
||||
NodeDeclaration &r_declaration);
|
||||
const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type);
|
||||
const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item);
|
||||
void values_to_simulation_state(const Span<NodeSimulationItem> node_simulation_items,
|
||||
const Span<void *> input_values,
|
||||
bke::sim::SimulationZoneState &r_zone_state);
|
||||
void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_items,
|
||||
const bke::sim::SimulationZoneState &zone_state,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &sim_output_node,
|
||||
Span<void *> r_output_values);
|
||||
void move_values_to_simulation_state(const Span<NodeSimulationItem> node_simulation_items,
|
||||
const Span<void *> input_values,
|
||||
bke::sim::SimulationZoneState &r_zone_state);
|
||||
void move_simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_items,
|
||||
bke::sim::SimulationZoneState &zone_state,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &sim_output_node,
|
||||
Span<void *> r_output_values);
|
||||
void copy_simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_items,
|
||||
const bke::sim::SimulationZoneState &zone_state,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &sim_output_node,
|
||||
Span<void *> r_output_values);
|
||||
|
||||
void copy_with_checked_indices(const GVArray &src,
|
||||
const VArray<int> &indices,
|
||||
|
||||
@@ -72,37 +72,74 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
|
||||
|
||||
const bke::sim::SimulationZoneID zone_id = get_simulation_zone_id(user_data, output_node_id_);
|
||||
|
||||
const bke::sim::SimulationZoneState *prev_zone_state =
|
||||
modifier_data.prev_simulation_state == nullptr ?
|
||||
nullptr :
|
||||
modifier_data.prev_simulation_state->get_zone_state(zone_id);
|
||||
|
||||
std::optional<bke::sim::SimulationZoneState> initial_prev_zone_state;
|
||||
if (prev_zone_state == nullptr) {
|
||||
Array<void *> input_values(simulation_items_.size(), nullptr);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
input_values[i] = params.try_get_input_data_ptr_or_request(i);
|
||||
}
|
||||
if (input_values.as_span().contains(nullptr)) {
|
||||
/* Wait until all inputs are available. */
|
||||
/* When caching is turned off and the old state doesn't need to persist, moving data
|
||||
* from the last state instead of copying it can avoid copies of geometry data arrays. */
|
||||
if (auto *state = modifier_data.prev_simulation_state_mutable) {
|
||||
if (bke::sim::SimulationZoneState *zone = state->get_zone_state(zone_id)) {
|
||||
this->output_simulation_state_move(params, user_data, *zone);
|
||||
return;
|
||||
}
|
||||
|
||||
initial_prev_zone_state.emplace();
|
||||
values_to_simulation_state(simulation_items_, input_values, *initial_prev_zone_state);
|
||||
prev_zone_state = &*initial_prev_zone_state;
|
||||
}
|
||||
|
||||
Array<void *> output_values(simulation_items_.size());
|
||||
/* If there is a read-only state from the last frame, output that directly. */
|
||||
if (const auto *state = modifier_data.prev_simulation_state) {
|
||||
if (const bke::sim::SimulationZoneState *zone = state->get_zone_state(zone_id)) {
|
||||
this->output_simulation_state_copy(params, user_data, *zone);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* When there is no previous state already, create the initial state. */
|
||||
Array<void *> input_values(simulation_items_.size(), nullptr);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
output_values[i] = params.get_output_data_ptr(i + 1);
|
||||
input_values[i] = params.try_get_input_data_ptr_or_request(i);
|
||||
}
|
||||
simulation_state_to_values(simulation_items_,
|
||||
*prev_zone_state,
|
||||
*modifier_data.self_object,
|
||||
*user_data.compute_context,
|
||||
node_,
|
||||
output_values);
|
||||
if (input_values.as_span().contains(nullptr)) {
|
||||
/* Wait until all inputs are available. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Instead of outputing the initial values directly, convert them to a simulation state and
|
||||
* then back. This ensures that the first frame behaves consistently with all other frames
|
||||
* which are necessarily stored in the simulation cache. */
|
||||
bke::sim::SimulationZoneState initial_zone_state;
|
||||
move_values_to_simulation_state(simulation_items_, input_values, initial_zone_state);
|
||||
this->output_simulation_state_move(params, user_data, initial_zone_state);
|
||||
}
|
||||
|
||||
void output_simulation_state_copy(lf::Params ¶ms,
|
||||
const GeoNodesLFUserData &user_data,
|
||||
const bke::sim::SimulationZoneState &state) const
|
||||
{
|
||||
Array<void *> outputs(simulation_items_.size());
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
outputs[i] = params.get_output_data_ptr(i + 1);
|
||||
}
|
||||
copy_simulation_state_to_values(simulation_items_,
|
||||
state,
|
||||
*user_data.modifier_data->self_object,
|
||||
*user_data.compute_context,
|
||||
node_,
|
||||
outputs);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
params.output_set(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void output_simulation_state_move(lf::Params ¶ms,
|
||||
const GeoNodesLFUserData &user_data,
|
||||
bke::sim::SimulationZoneState &state) const
|
||||
{
|
||||
Array<void *> outputs(simulation_items_.size());
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
outputs[i] = params.get_output_data_ptr(i + 1);
|
||||
}
|
||||
move_simulation_state_to_values(simulation_items_,
|
||||
state,
|
||||
*user_data.modifier_data->self_object,
|
||||
*user_data.compute_context,
|
||||
node_,
|
||||
outputs);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
params.output_set(i + 1);
|
||||
}
|
||||
|
||||
@@ -176,103 +176,13 @@ static void cleanup_geometry_for_simulation_state(GeometrySet &main_geometry)
|
||||
});
|
||||
}
|
||||
|
||||
void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_items,
|
||||
const bke::sim::SimulationZoneState &zone_state,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &node,
|
||||
Span<void *> r_output_values)
|
||||
/**
|
||||
* Some attributes stored in the simulation state become anonymous attributes in geometry nodes.
|
||||
* This maps attribute names to their corresponding anonymous attribute ids.
|
||||
*/
|
||||
static void rename_attributes(const Span<GeometrySet *> geometries,
|
||||
const Map<std::string, AnonymousAttributeIDPtr> &attribute_map)
|
||||
{
|
||||
/* Some attributes stored in the simulation state become anonymous attributes in geometry nodes.
|
||||
* This maps attribute names to their corresponding anonymous attribute ids. */
|
||||
Map<std::string, AnonymousAttributeIDPtr> attribute_map;
|
||||
Vector<GeometrySet *> geometries;
|
||||
|
||||
for (const int i : node_simulation_items.index_range()) {
|
||||
const NodeSimulationItem &item = node_simulation_items[i];
|
||||
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
|
||||
const CPPType &cpp_type = get_simulation_item_cpp_type(socket_type);
|
||||
|
||||
void *r_output_value = r_output_values[i];
|
||||
|
||||
if (!zone_state.item_by_identifier.contains(item.identifier)) {
|
||||
cpp_type.value_initialize(r_output_value);
|
||||
continue;
|
||||
}
|
||||
const bke::sim::SimulationStateItem &state_item = *zone_state.item_by_identifier.lookup(
|
||||
item.identifier);
|
||||
|
||||
switch (socket_type) {
|
||||
case SOCK_GEOMETRY: {
|
||||
if (const auto *geo_state_item =
|
||||
dynamic_cast<const bke::sim::GeometrySimulationStateItem *>(&state_item))
|
||||
{
|
||||
GeometrySet *geometry = new (r_output_value) GeometrySet(geo_state_item->geometry);
|
||||
geometries.append(geometry);
|
||||
}
|
||||
else {
|
||||
cpp_type.value_initialize(r_output_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOCK_FLOAT:
|
||||
case SOCK_VECTOR:
|
||||
case SOCK_INT:
|
||||
case SOCK_BOOLEAN:
|
||||
case SOCK_ROTATION:
|
||||
case SOCK_RGBA: {
|
||||
const fn::ValueOrFieldCPPType &value_or_field_type =
|
||||
*fn::ValueOrFieldCPPType::get_from_self(cpp_type);
|
||||
if (const auto *primitive_state_item =
|
||||
dynamic_cast<const bke::sim::PrimitiveSimulationStateItem *>(&state_item))
|
||||
{
|
||||
if (primitive_state_item->type() == value_or_field_type.value) {
|
||||
value_or_field_type.construct_from_value(r_output_value,
|
||||
primitive_state_item->value());
|
||||
}
|
||||
else {
|
||||
cpp_type.value_initialize(r_output_value);
|
||||
}
|
||||
}
|
||||
else if (const auto *attribute_state_item =
|
||||
dynamic_cast<const bke::sim::AttributeSimulationStateItem *>(&state_item))
|
||||
{
|
||||
AnonymousAttributeIDPtr attribute_id = MEM_new<NodeAnonymousAttributeID>(
|
||||
__func__,
|
||||
self_object,
|
||||
compute_context,
|
||||
node,
|
||||
std::to_string(item.identifier),
|
||||
item.name);
|
||||
GField field{std::make_shared<AnonymousAttributeFieldInput>(
|
||||
attribute_id, value_or_field_type.value, node.label_or_name())};
|
||||
value_or_field_type.construct_from_field(r_output_value, std::move(field));
|
||||
attribute_map.add(attribute_state_item->name(), std::move(attribute_id));
|
||||
}
|
||||
else {
|
||||
cpp_type.value_initialize(r_output_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
if (const auto *string_state_item =
|
||||
dynamic_cast<const bke::sim::StringSimulationStateItem *>(&state_item))
|
||||
{
|
||||
new (r_output_value) ValueOrField<std::string>(string_state_item->value());
|
||||
}
|
||||
else {
|
||||
cpp_type.value_initialize(r_output_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
cpp_type.value_initialize(r_output_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make some attributes anonymous. */
|
||||
for (GeometrySet *geometry : geometries) {
|
||||
for (const GeometryComponent::Type type : {GeometryComponent::Type::Mesh,
|
||||
GeometryComponent::Type::Curve,
|
||||
@@ -282,6 +192,16 @@ void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_i
|
||||
if (!geometry->has(type)) {
|
||||
continue;
|
||||
}
|
||||
/* Avoid write access on the geometry when unnecessary to avoid copying data-blocks. */
|
||||
const AttributeAccessor attributes_read_only =
|
||||
*geometry->get_component_for_read(type)->attributes();
|
||||
if (std::none_of(attribute_map.keys().begin(),
|
||||
attribute_map.keys().end(),
|
||||
[&](const StringRef name) { return attributes_read_only.contains(name); }))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GeometryComponent &component = geometry->get_component_for_write(type);
|
||||
MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
for (const MapItem<std::string, AnonymousAttributeIDPtr> &attribute_item :
|
||||
@@ -292,9 +212,162 @@ void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_i
|
||||
}
|
||||
}
|
||||
|
||||
void values_to_simulation_state(const Span<NodeSimulationItem> node_simulation_items,
|
||||
const Span<void *> input_values,
|
||||
bke::sim::SimulationZoneState &r_zone_state)
|
||||
static bool copy_value_or_field_simulation_state_to_value(
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &node,
|
||||
const NodeSimulationItem &sim_item,
|
||||
const CPPType &cpp_type,
|
||||
const bke::sim::SimulationStateItem &state_item,
|
||||
Vector<GeometrySet *> &r_geometries,
|
||||
Map<std::string, AnonymousAttributeIDPtr> &r_attribute_map,
|
||||
void *r_output_value)
|
||||
{
|
||||
switch (eNodeSocketDatatype(sim_item.socket_type)) {
|
||||
case SOCK_GEOMETRY: {
|
||||
if (const auto *item = dynamic_cast<const bke::sim::GeometrySimulationStateItem *>(
|
||||
&state_item)) {
|
||||
GeometrySet *geometry = new (r_output_value) GeometrySet(item->geometry);
|
||||
r_geometries.append(geometry);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOCK_FLOAT:
|
||||
case SOCK_VECTOR:
|
||||
case SOCK_INT:
|
||||
case SOCK_BOOLEAN:
|
||||
case SOCK_ROTATION:
|
||||
case SOCK_RGBA: {
|
||||
const fn::ValueOrFieldCPPType &value_or_field_type = *fn::ValueOrFieldCPPType::get_from_self(
|
||||
cpp_type);
|
||||
if (const auto *item = dynamic_cast<const bke::sim::PrimitiveSimulationStateItem *>(
|
||||
&state_item)) {
|
||||
if (item->type() == value_or_field_type.value) {
|
||||
value_or_field_type.construct_from_value(r_output_value, item->value());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (const auto *item = dynamic_cast<const bke::sim::AttributeSimulationStateItem *>(
|
||||
&state_item)) {
|
||||
AnonymousAttributeIDPtr attribute_id = MEM_new<NodeAnonymousAttributeID>(
|
||||
__func__,
|
||||
self_object,
|
||||
compute_context,
|
||||
node,
|
||||
std::to_string(sim_item.identifier),
|
||||
sim_item.name);
|
||||
GField field{std::make_shared<AnonymousAttributeFieldInput>(
|
||||
attribute_id, value_or_field_type.value, node.label_or_name())};
|
||||
value_or_field_type.construct_from_field(r_output_value, std::move(field));
|
||||
r_attribute_map.add(item->name(), std::move(attribute_id));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
if (const auto *item = dynamic_cast<const bke::sim::StringSimulationStateItem *>(
|
||||
&state_item)) {
|
||||
new (r_output_value) ValueOrField<std::string>(item->value());
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void move_simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_items,
|
||||
bke::sim::SimulationZoneState &zone_state,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &node,
|
||||
Span<void *> r_output_values)
|
||||
{
|
||||
Map<std::string, AnonymousAttributeIDPtr> attribute_map;
|
||||
Vector<GeometrySet *> geometries;
|
||||
|
||||
for (const int i : node_simulation_items.index_range()) {
|
||||
const NodeSimulationItem &sim_item = node_simulation_items[i];
|
||||
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(sim_item.socket_type);
|
||||
const CPPType &cpp_type = get_simulation_item_cpp_type(socket_type);
|
||||
const std::unique_ptr<bke::sim::SimulationStateItem> *state_item =
|
||||
zone_state.item_by_identifier.lookup_ptr(sim_item.identifier);
|
||||
if (!state_item) {
|
||||
cpp_type.value_initialize(r_output_values[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (socket_type == SOCK_GEOMETRY) {
|
||||
if (auto *item = dynamic_cast<bke::sim::GeometrySimulationStateItem *>(state_item->get())) {
|
||||
GeometrySet *geometry = new (r_output_values[i]) GeometrySet(std::move(item->geometry));
|
||||
geometries.append(geometry);
|
||||
}
|
||||
else {
|
||||
cpp_type.value_initialize(r_output_values[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!copy_value_or_field_simulation_state_to_value(self_object,
|
||||
compute_context,
|
||||
node,
|
||||
sim_item,
|
||||
cpp_type,
|
||||
*state_item->get(),
|
||||
geometries,
|
||||
attribute_map,
|
||||
r_output_values[i]))
|
||||
{
|
||||
cpp_type.value_initialize(r_output_values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rename_attributes(geometries, attribute_map);
|
||||
}
|
||||
|
||||
void copy_simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_items,
|
||||
const bke::sim::SimulationZoneState &zone_state,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &node,
|
||||
Span<void *> r_output_values)
|
||||
{
|
||||
Map<std::string, AnonymousAttributeIDPtr> attribute_map;
|
||||
Vector<GeometrySet *> geometries;
|
||||
|
||||
for (const int i : node_simulation_items.index_range()) {
|
||||
const NodeSimulationItem &sim_item = node_simulation_items[i];
|
||||
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(sim_item.socket_type);
|
||||
const CPPType &cpp_type = get_simulation_item_cpp_type(socket_type);
|
||||
const std::unique_ptr<bke::sim::SimulationStateItem> *state_item =
|
||||
zone_state.item_by_identifier.lookup_ptr(sim_item.identifier);
|
||||
if (!state_item) {
|
||||
cpp_type.value_initialize(r_output_values[i]);
|
||||
continue;
|
||||
}
|
||||
if (!copy_value_or_field_simulation_state_to_value(self_object,
|
||||
compute_context,
|
||||
node,
|
||||
sim_item,
|
||||
cpp_type,
|
||||
*state_item->get(),
|
||||
geometries,
|
||||
attribute_map,
|
||||
r_output_values[i]))
|
||||
{
|
||||
cpp_type.value_initialize(r_output_values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
rename_attributes(geometries, attribute_map);
|
||||
}
|
||||
|
||||
void move_values_to_simulation_state(const Span<NodeSimulationItem> node_simulation_items,
|
||||
const Span<void *> input_values,
|
||||
bke::sim::SimulationZoneState &r_zone_state)
|
||||
{
|
||||
Vector<GeometrySet *> stored_geometries;
|
||||
|
||||
@@ -712,8 +785,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
nullptr;
|
||||
if (eval_data.is_first_evaluation && current_zone_state != nullptr) {
|
||||
/* Common case when data is cached already. */
|
||||
this->output_cached_state(
|
||||
params, *modifier_data.self_object, *user_data.compute_context, *current_zone_state);
|
||||
this->output_cached_state(params, user_data, *current_zone_state);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -734,8 +806,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
nullptr;
|
||||
if (next_zone_state == nullptr) {
|
||||
/* Output the last cached simulation state. */
|
||||
this->output_cached_state(
|
||||
params, *modifier_data.self_object, *user_data.compute_context, *prev_zone_state);
|
||||
this->output_cached_state(params, user_data, *prev_zone_state);
|
||||
return;
|
||||
}
|
||||
/* A previous and next frame is cached already, but the current frame is not. */
|
||||
@@ -762,22 +833,24 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
/* Wait until all inputs are available. */
|
||||
return;
|
||||
}
|
||||
values_to_simulation_state(simulation_items_, input_values, new_zone_state);
|
||||
this->output_cached_state(
|
||||
params, *modifier_data.self_object, *user_data.compute_context, new_zone_state);
|
||||
move_values_to_simulation_state(simulation_items_, input_values, new_zone_state);
|
||||
this->output_cached_state(params, user_data, new_zone_state);
|
||||
}
|
||||
|
||||
void output_cached_state(lf::Params ¶ms,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
GeoNodesLFUserData &user_data,
|
||||
const bke::sim::SimulationZoneState &state) const
|
||||
{
|
||||
Array<void *> output_values(simulation_items_.size());
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
output_values[i] = params.get_output_data_ptr(i);
|
||||
}
|
||||
simulation_state_to_values(
|
||||
simulation_items_, state, self_object, compute_context, node_, output_values);
|
||||
copy_simulation_state_to_values(simulation_items_,
|
||||
state,
|
||||
*user_data.modifier_data->self_object,
|
||||
*user_data.compute_context,
|
||||
node_,
|
||||
output_values);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
params.output_set(i);
|
||||
}
|
||||
@@ -794,7 +867,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
output_values[i] = params.get_output_data_ptr(i);
|
||||
}
|
||||
simulation_state_to_values(
|
||||
copy_simulation_state_to_values(
|
||||
simulation_items_, prev_state, self_object, compute_context, node_, output_values);
|
||||
|
||||
Array<void *> next_values(simulation_items_.size());
|
||||
@@ -803,7 +876,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
const CPPType &type = *outputs_[i].type;
|
||||
next_values[i] = allocator.allocate(type.size(), type.alignment());
|
||||
}
|
||||
simulation_state_to_values(
|
||||
copy_simulation_state_to_values(
|
||||
simulation_items_, next_state, self_object, compute_context, node_, next_values);
|
||||
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
|
||||
Reference in New Issue
Block a user