2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-03-29 14:16:31 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup nodes
|
|
|
|
|
*
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
* This file mainly converts a #bNodeTree into a lazy-function graph, that can then be evaluated to
|
|
|
|
|
* execute geometry nodes. This generally works by creating a lazy-function for every node, which
|
|
|
|
|
* is then put into the lazy-function graph. Then the nodes in the new graph are linked based on
|
|
|
|
|
* links in the original #bNodeTree. Some additional nodes are inserted for things like type
|
|
|
|
|
* conversions and multi-input sockets.
|
|
|
|
|
*
|
|
|
|
|
* If the #bNodeTree contains zones, those are turned into separate lazy-functions first.
|
|
|
|
|
* Essentially, a separate lazy-function graph is created for every zone that is than called by the
|
|
|
|
|
* parent zone or by the root graph.
|
2022-09-13 08:44:26 +02:00
|
|
|
*
|
|
|
|
|
* Currently, lazy-functions are even created for nodes that don't strictly require it, like
|
|
|
|
|
* reroutes or muted nodes. In the future we could avoid that at the cost of additional code
|
|
|
|
|
* complexity. So far, this does not seem to be a performance issue.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "NOD_geometry_exec.hh"
|
|
|
|
|
#include "NOD_geometry_nodes_lazy_function.hh"
|
|
|
|
|
#include "NOD_multi_function.hh"
|
|
|
|
|
#include "NOD_node_declaration.hh"
|
|
|
|
|
|
2023-10-06 23:00:29 +02:00
|
|
|
#include "BLI_array_utils.hh"
|
2023-04-28 16:53:03 +02:00
|
|
|
#include "BLI_bit_group_vector.hh"
|
|
|
|
|
#include "BLI_bit_span_ops.hh"
|
2022-11-12 18:33:31 +01:00
|
|
|
#include "BLI_cpp_types.hh"
|
2023-01-05 14:05:30 +01:00
|
|
|
#include "BLI_dot_export.hh"
|
|
|
|
|
#include "BLI_hash.h"
|
2024-02-02 19:55:06 +01:00
|
|
|
#include "BLI_hash_md5.hh"
|
2022-09-20 10:59:12 +02:00
|
|
|
#include "BLI_lazy_threading.hh"
|
2022-09-13 08:44:26 +02:00
|
|
|
#include "BLI_map.hh"
|
|
|
|
|
|
|
|
|
|
#include "DNA_ID.h"
|
|
|
|
|
|
|
|
|
|
#include "BKE_compute_contexts.hh"
|
|
|
|
|
#include "BKE_geometry_set.hh"
|
2023-12-17 14:00:07 +01:00
|
|
|
#include "BKE_node_socket_value.hh"
|
2023-06-14 13:56:57 +02:00
|
|
|
#include "BKE_node_tree_anonymous_attributes.hh"
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
#include "BKE_node_tree_zones.hh"
|
2022-09-13 08:44:26 +02:00
|
|
|
#include "BKE_type_conversions.hh"
|
|
|
|
|
|
2023-07-11 22:36:10 +02:00
|
|
|
#include "FN_lazy_function_execute.hh"
|
2022-09-13 08:44:26 +02:00
|
|
|
#include "FN_lazy_function_graph_executor.hh"
|
|
|
|
|
|
2023-09-22 03:18:17 +02:00
|
|
|
#include "DEG_depsgraph_query.hh"
|
2022-10-03 19:15:06 +02:00
|
|
|
|
2022-09-17 01:50:55 +02:00
|
|
|
#include <fmt/format.h>
|
Cleanup: fewer iostreams related includes from BLI/BKE headers
Including <iostream> or similar headers is quite expensive, since it
also pulls in things like <locale> and so on. In many BLI headers,
iostreams are only used to implement some sort of "debug print",
or an operator<< for ostream.
Change some of the commonly used places to instead include <iosfwd>,
which is the standard way of forward-declaring iostreams related
classes, and move the actual debug-print / operator<< implementations
into .cc files.
This is not done for templated classes though (it would be possible
to provide explicit operator<< instantiations somewhere in the
source file, but that would lead to hard-to-figure-out linker error
whenever someone would add a different template type). There, where
possible, I changed from full <iostream> include to only the needed
<ostream> part.
For Span<T>, I just removed print_as_lines since it's not used by
anything. It could be moved into a .cc file using a similar approach
as above if needed.
Doing full blender build changes include counts this way:
- <iostream> 1986 -> 978
- <sstream> 2880 -> 925
It does not affect the total build time much though, mostly because
towards the end of it there's just several CPU cores finishing
compiling OpenVDB related source files.
Pull Request: https://projects.blender.org/blender/blender/pulls/111046
2023-08-11 12:27:56 +03:00
|
|
|
#include <sstream>
|
2022-09-17 01:50:55 +02:00
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
namespace blender::nodes {
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
namespace aai = bke::anonymous_attribute_inferencing;
|
2023-06-20 10:25:41 +02:00
|
|
|
using bke::bNodeTreeZone;
|
|
|
|
|
using bke::bNodeTreeZones;
|
2023-12-13 13:40:40 +01:00
|
|
|
using bke::SocketValueVariant;
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
static const CPPType *get_socket_cpp_type(const bNodeSocketType &typeinfo)
|
|
|
|
|
{
|
|
|
|
|
const CPPType *type = typeinfo.geometry_nodes_cpp_type;
|
|
|
|
|
if (type == nullptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
BLI_assert(type->has_special_member_functions());
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const CPPType *get_socket_cpp_type(const bNodeSocket &socket)
|
|
|
|
|
{
|
|
|
|
|
return get_socket_cpp_type(*socket.typeinfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const CPPType *get_vector_type(const CPPType &type)
|
|
|
|
|
{
|
2022-11-12 18:33:31 +01:00
|
|
|
const VectorCPPType *vector_type = VectorCPPType::get_from_value(type);
|
|
|
|
|
if (vector_type == nullptr) {
|
|
|
|
|
return nullptr;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2022-11-12 18:33:31 +01:00
|
|
|
return &vector_type->self;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks which sockets of the node are available and creates corresponding inputs/outputs on the
|
|
|
|
|
* lazy-function.
|
|
|
|
|
*/
|
|
|
|
|
static void lazy_function_interface_from_node(const bNode &node,
|
|
|
|
|
Vector<lf::Input> &r_inputs,
|
2023-04-22 16:14:38 +02:00
|
|
|
Vector<lf::Output> &r_outputs,
|
|
|
|
|
MutableSpan<int> r_lf_index_by_bsocket)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
const bool is_muted = node.is_muted();
|
2023-03-14 14:09:29 +01:00
|
|
|
const lf::ValueUsage input_usage = lf::ValueUsage::Used;
|
2022-09-13 08:44:26 +02:00
|
|
|
for (const bNodeSocket *socket : node.input_sockets()) {
|
|
|
|
|
if (!socket->is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const CPPType *type = get_socket_cpp_type(*socket);
|
|
|
|
|
if (type == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (socket->is_multi_input() && !is_muted) {
|
|
|
|
|
type = get_vector_type(*type);
|
|
|
|
|
}
|
2023-04-22 16:14:38 +02:00
|
|
|
r_lf_index_by_bsocket[socket->index_in_tree()] = r_inputs.append_and_get_index_as(
|
2023-10-06 18:29:13 +02:00
|
|
|
socket->name, *type, input_usage);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
for (const bNodeSocket *socket : node.output_sockets()) {
|
|
|
|
|
if (!socket->is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const CPPType *type = get_socket_cpp_type(*socket);
|
|
|
|
|
if (type == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-04-22 16:14:38 +02:00
|
|
|
r_lf_index_by_bsocket[socket->index_in_tree()] = r_outputs.append_and_get_index_as(
|
2023-10-06 18:29:13 +02:00
|
|
|
socket->name, *type);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
NodeAnonymousAttributeID::NodeAnonymousAttributeID(const Object &object,
|
|
|
|
|
const ComputeContext &compute_context,
|
|
|
|
|
const bNode &bnode,
|
|
|
|
|
const StringRef identifier,
|
|
|
|
|
const StringRef name)
|
|
|
|
|
: socket_name_(name)
|
|
|
|
|
{
|
|
|
|
|
const ComputeContextHash &hash = compute_context.hash();
|
|
|
|
|
{
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << hash << "_" << object.id.name << "_" << bnode.identifier << "_" << identifier;
|
|
|
|
|
long_name_ = ss.str();
|
2023-04-24 11:42:05 +02:00
|
|
|
}
|
|
|
|
|
{
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
uint64_t hash_result[2];
|
|
|
|
|
BLI_hash_md5_buffer(long_name_.data(), long_name_.size(), hash_result);
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << ".a_" << std::hex << hash_result[0] << hash_result[1];
|
|
|
|
|
name_ = ss.str();
|
|
|
|
|
BLI_assert(name_.size() < MAX_CUSTOMDATA_LAYER_NAME);
|
2023-04-24 11:42:05 +02:00
|
|
|
}
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string NodeAnonymousAttributeID::user_name() const
|
|
|
|
|
{
|
|
|
|
|
return socket_name_;
|
|
|
|
|
}
|
2023-04-24 11:42:05 +02:00
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
/**
|
|
|
|
|
* Used for most normal geometry nodes like Subdivision Surface and Set Position.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForGeometryNode : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const bNode &node_;
|
2023-04-26 10:42:23 +02:00
|
|
|
const GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info_;
|
2023-04-24 11:42:05 +02:00
|
|
|
/**
|
|
|
|
|
* A bool for every output bsocket. If true, the socket just outputs a field containing an
|
|
|
|
|
* anonymous attribute id. If only such outputs are requested by other nodes, the node itself
|
|
|
|
|
* does not have to execute.
|
|
|
|
|
*/
|
|
|
|
|
Vector<bool> is_attribute_output_bsocket_;
|
|
|
|
|
|
|
|
|
|
struct OutputAttributeID {
|
|
|
|
|
int bsocket_index;
|
|
|
|
|
AnonymousAttributeIDPtr attribute_id;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Storage {
|
|
|
|
|
Vector<OutputAttributeID, 1> attributes;
|
|
|
|
|
};
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-04-22 13:01:00 +02:00
|
|
|
public:
|
2022-09-13 08:44:26 +02:00
|
|
|
LazyFunctionForGeometryNode(const bNode &node,
|
2023-04-26 10:42:23 +02:00
|
|
|
GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
|
2023-04-22 13:01:00 +02:00
|
|
|
: node_(node),
|
2023-04-26 10:42:23 +02:00
|
|
|
own_lf_graph_info_(own_lf_graph_info),
|
2023-04-24 11:42:05 +02:00
|
|
|
is_attribute_output_bsocket_(node.output_sockets().size(), false)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
BLI_assert(node.typeinfo->geometry_node_execute != nullptr);
|
|
|
|
|
debug_name_ = node.name;
|
2023-04-26 10:42:23 +02:00
|
|
|
lazy_function_interface_from_node(
|
|
|
|
|
node, inputs_, outputs_, own_lf_graph_info.mapping.lf_index_by_bsocket);
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
const NodeDeclaration &node_decl = *node.declaration();
|
|
|
|
|
const aal::RelationsInNode *relations = node_decl.anonymous_attribute_relations();
|
|
|
|
|
if (relations == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-24 11:42:05 +02:00
|
|
|
if (!relations->available_relations.is_empty()) {
|
|
|
|
|
/* Inputs are only used when an output is used that is not just outputting an anonymous
|
|
|
|
|
* attribute field. */
|
|
|
|
|
for (lf::Input &input : inputs_) {
|
|
|
|
|
input.usage = lf::ValueUsage::Maybe;
|
|
|
|
|
}
|
|
|
|
|
for (const aal::AvailableRelation &relation : relations->available_relations) {
|
|
|
|
|
is_attribute_output_bsocket_[relation.field_output] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
Vector<const bNodeSocket *> handled_field_outputs;
|
|
|
|
|
for (const aal::AvailableRelation &relation : relations->available_relations) {
|
|
|
|
|
const bNodeSocket &output_bsocket = node.output_socket(relation.field_output);
|
|
|
|
|
if (output_bsocket.is_available() && !handled_field_outputs.contains(&output_bsocket)) {
|
|
|
|
|
handled_field_outputs.append(&output_bsocket);
|
|
|
|
|
const int lf_index = inputs_.append_and_get_index_as("Output Used", CPPType::get<bool>());
|
2023-04-26 10:42:23 +02:00
|
|
|
own_lf_graph_info.mapping
|
|
|
|
|
.lf_input_index_for_output_bsocket_usage[output_bsocket.index_in_all_outputs()] =
|
|
|
|
|
lf_index;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<const bNodeSocket *> handled_geometry_outputs;
|
|
|
|
|
for (const aal::PropagateRelation &relation : relations->propagate_relations) {
|
|
|
|
|
const bNodeSocket &output_bsocket = node.output_socket(relation.to_geometry_output);
|
|
|
|
|
if (output_bsocket.is_available() && !handled_geometry_outputs.contains(&output_bsocket)) {
|
|
|
|
|
handled_geometry_outputs.append(&output_bsocket);
|
|
|
|
|
const int lf_index = inputs_.append_and_get_index_as(
|
|
|
|
|
"Propagate to Output", CPPType::get<bke::AnonymousAttributeSet>());
|
2023-04-26 10:42:23 +02:00
|
|
|
own_lf_graph_info.mapping.lf_input_index_for_attribute_propagation_to_output
|
|
|
|
|
[output_bsocket.index_in_all_outputs()] = lf_index;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-24 11:42:05 +02:00
|
|
|
void *init_storage(LinearAllocator<> &allocator) const override
|
|
|
|
|
{
|
|
|
|
|
return allocator.construct<Storage>().release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void destruct_storage(void *storage) const override
|
|
|
|
|
{
|
|
|
|
|
Storage *s = static_cast<Storage *>(storage);
|
|
|
|
|
std::destroy_at(s);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-04 18:22:45 +02:00
|
|
|
static const Object *get_self_object(const GeoNodesLFUserData &user_data)
|
|
|
|
|
{
|
2023-11-29 13:22:20 +01:00
|
|
|
if (user_data.call_data->modifier_data) {
|
|
|
|
|
return user_data.call_data->modifier_data->self_object;
|
2023-08-04 18:22:45 +02:00
|
|
|
}
|
2023-11-29 13:22:20 +01:00
|
|
|
if (user_data.call_data->operator_data) {
|
|
|
|
|
return user_data.call_data->operator_data->self_object;
|
2023-08-04 18:22:45 +02:00
|
|
|
}
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
|
|
|
|
{
|
2023-04-24 11:42:05 +02:00
|
|
|
Storage *storage = static_cast<Storage *>(context.storage);
|
2022-09-13 08:44:26 +02:00
|
|
|
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
BLI_assert(user_data != nullptr);
|
2023-05-09 13:10:47 +02:00
|
|
|
const auto &local_user_data = *static_cast<GeoNodesLFLocalUserData *>(context.local_user_data);
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-04-24 11:42:05 +02:00
|
|
|
/* Lazily create the required anonymous attribute ids. */
|
|
|
|
|
auto get_output_attribute_id = [&](const int output_bsocket_index) -> AnonymousAttributeIDPtr {
|
|
|
|
|
for (const OutputAttributeID &node_output_attribute : storage->attributes) {
|
|
|
|
|
if (node_output_attribute.bsocket_index == output_bsocket_index) {
|
|
|
|
|
return node_output_attribute.attribute_id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const bNodeSocket &bsocket = node_.output_socket(output_bsocket_index);
|
2023-11-27 15:53:29 +01:00
|
|
|
AnonymousAttributeIDPtr attribute_id = AnonymousAttributeIDPtr(
|
|
|
|
|
MEM_new<NodeAnonymousAttributeID>(__func__,
|
|
|
|
|
*this->get_self_object(*user_data),
|
|
|
|
|
*user_data->compute_context,
|
|
|
|
|
node_,
|
|
|
|
|
bsocket.identifier,
|
|
|
|
|
bsocket.name));
|
2023-04-24 11:42:05 +02:00
|
|
|
storage->attributes.append({output_bsocket_index, attribute_id});
|
|
|
|
|
return attribute_id;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool used_non_attribute_output_exists = false;
|
|
|
|
|
for (const int output_bsocket_index : node_.output_sockets().index_range()) {
|
|
|
|
|
const bNodeSocket &output_bsocket = node_.output_socket(output_bsocket_index);
|
2023-04-26 10:42:23 +02:00
|
|
|
const int lf_index =
|
|
|
|
|
own_lf_graph_info_.mapping.lf_index_by_bsocket[output_bsocket.index_in_tree()];
|
2023-04-24 11:42:05 +02:00
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const lf::ValueUsage output_usage = params.get_output_usage(lf_index);
|
|
|
|
|
if (output_usage == lf::ValueUsage::Unused) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (is_attribute_output_bsocket_[output_bsocket_index]) {
|
|
|
|
|
if (params.output_was_set(lf_index)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
this->output_anonymous_attribute_field(
|
2023-12-17 14:00:07 +01:00
|
|
|
params, lf_index, output_bsocket, get_output_attribute_id(output_bsocket_index));
|
2023-04-24 11:42:05 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (output_usage == lf::ValueUsage::Used) {
|
|
|
|
|
used_non_attribute_output_exists = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!used_non_attribute_output_exists) {
|
|
|
|
|
/* Only attribute outputs are used currently, no need to evaluate the full node and its
|
|
|
|
|
* inputs. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool missing_input = false;
|
|
|
|
|
for (const int lf_index : inputs_.index_range()) {
|
|
|
|
|
if (params.try_get_input_data_ptr_or_request(lf_index) == nullptr) {
|
|
|
|
|
missing_input = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (missing_input) {
|
|
|
|
|
/* Wait until all inputs are available. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-26 10:42:23 +02:00
|
|
|
GeoNodeExecParams geo_params{
|
|
|
|
|
node_,
|
|
|
|
|
params,
|
|
|
|
|
context,
|
|
|
|
|
own_lf_graph_info_.mapping.lf_input_index_for_output_bsocket_usage,
|
|
|
|
|
own_lf_graph_info_.mapping.lf_input_index_for_attribute_propagation_to_output,
|
|
|
|
|
get_output_attribute_id};
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now();
|
|
|
|
|
node_.typeinfo->geometry_node_execute(geo_params);
|
|
|
|
|
geo_eval_log::TimePoint end_time = geo_eval_log::Clock::now();
|
|
|
|
|
|
2023-09-21 14:35:20 +02:00
|
|
|
if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(*user_data))
|
|
|
|
|
{
|
2024-02-28 22:22:21 +01:00
|
|
|
tree_logger->node_execution_times.append(*tree_logger->allocator,
|
|
|
|
|
{node_.identifier, start_time, end_time});
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-04-24 11:42:05 +02:00
|
|
|
/**
|
|
|
|
|
* Output the given anonymous attribute id as a field.
|
|
|
|
|
*/
|
|
|
|
|
void output_anonymous_attribute_field(lf::Params ¶ms,
|
|
|
|
|
const int lf_index,
|
2023-12-17 14:00:07 +01:00
|
|
|
const bNodeSocket &bsocket,
|
2023-04-24 11:42:05 +02:00
|
|
|
AnonymousAttributeIDPtr attribute_id) const
|
|
|
|
|
{
|
2022-09-17 01:50:55 +02:00
|
|
|
GField output_field{std::make_shared<AnonymousAttributeFieldInput>(
|
|
|
|
|
std::move(attribute_id),
|
2023-12-17 14:00:07 +01:00
|
|
|
*bsocket.typeinfo->base_cpp_type,
|
2024-02-03 19:14:51 +01:00
|
|
|
fmt::format(TIP_("{} node"), node_.label_or_name()))};
|
2023-04-24 11:42:05 +02:00
|
|
|
void *r_value = params.get_output_data_ptr(lf_index);
|
2023-12-17 14:00:07 +01:00
|
|
|
new (r_value) SocketValueVariant(std::move(output_field));
|
2023-04-24 11:42:05 +02:00
|
|
|
params.output_set(lf_index);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
std::string input_name(const int index) const override
|
|
|
|
|
{
|
2023-04-22 13:01:00 +02:00
|
|
|
for (const bNodeSocket *bsocket : node_.output_sockets()) {
|
|
|
|
|
{
|
2023-04-26 10:42:23 +02:00
|
|
|
const int lf_index =
|
|
|
|
|
own_lf_graph_info_.mapping
|
|
|
|
|
.lf_input_index_for_output_bsocket_usage[bsocket->index_in_all_outputs()];
|
2023-04-22 13:01:00 +02:00
|
|
|
if (index == lf_index) {
|
2023-10-06 18:29:13 +02:00
|
|
|
return StringRef("Use Output '") + bsocket->name + "'";
|
2023-04-22 13:01:00 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-04-22 13:01:00 +02:00
|
|
|
{
|
|
|
|
|
const int lf_index =
|
2023-04-26 10:42:23 +02:00
|
|
|
own_lf_graph_info_.mapping.lf_input_index_for_attribute_propagation_to_output
|
|
|
|
|
[bsocket->index_in_all_outputs()];
|
2023-04-22 13:01:00 +02:00
|
|
|
if (index == lf_index) {
|
2023-10-06 18:29:13 +02:00
|
|
|
return StringRef("Propagate to '") + bsocket->name + "'";
|
2023-04-22 13:01:00 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return inputs_[index].debug_name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string output_name(const int index) const override
|
|
|
|
|
{
|
|
|
|
|
return outputs_[index].debug_name;
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2022-09-22 23:59:36 -05:00
|
|
|
* Used to gather all inputs of a multi-input socket. A separate node is necessary because
|
2022-09-13 08:44:26 +02:00
|
|
|
* multi-inputs are not supported in lazy-function graphs.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForMultiInput : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const CPPType *base_type_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForMultiInput(const bNodeSocket &socket)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Multi Input";
|
|
|
|
|
base_type_ = get_socket_cpp_type(socket);
|
|
|
|
|
BLI_assert(base_type_ != nullptr);
|
|
|
|
|
BLI_assert(socket.is_multi_input());
|
2022-10-17 12:00:09 +02:00
|
|
|
const bNodeTree &btree = socket.owner_tree();
|
2022-09-13 08:44:26 +02:00
|
|
|
for (const bNodeLink *link : socket.directly_linked_links()) {
|
2022-12-14 18:28:07 +01:00
|
|
|
if (link->is_muted() || !link->fromsock->is_available() ||
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeIsDanglingReroute(&btree, link->fromnode))
|
2022-12-14 18:28:07 +01:00
|
|
|
{
|
|
|
|
|
continue;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2022-12-14 18:28:07 +01:00
|
|
|
inputs_.append({"Input", *base_type_});
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
const CPPType *vector_type = get_vector_type(*base_type_);
|
|
|
|
|
BLI_assert(vector_type != nullptr);
|
|
|
|
|
outputs_.append({"Output", *vector_type});
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
/* Currently we only have multi-inputs for geometry and value sockets. This could be
|
2022-09-13 08:44:26 +02:00
|
|
|
* generalized in the future. */
|
2023-12-17 14:00:07 +01:00
|
|
|
base_type_->to_static_type_tag<GeometrySet, SocketValueVariant>([&](auto type_tag) {
|
|
|
|
|
using T = typename decltype(type_tag)::type;
|
|
|
|
|
if constexpr (std::is_void_v<T>) {
|
|
|
|
|
/* This type is not supported in this node for now. */
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
void *output_ptr = params.get_output_data_ptr(0);
|
|
|
|
|
Vector<T> &values = *new (output_ptr) Vector<T>();
|
|
|
|
|
for (const int i : inputs_.index_range()) {
|
|
|
|
|
values.append(params.extract_input<T>(i));
|
|
|
|
|
}
|
|
|
|
|
params.output_set(0);
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Simple lazy-function that just forwards the input.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForRerouteNode : public LazyFunction {
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForRerouteNode(const CPPType &type)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Reroute";
|
|
|
|
|
inputs_.append({"Input", type});
|
|
|
|
|
outputs_.append({"Output", type});
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
void *input_value = params.try_get_input_data_ptr(0);
|
|
|
|
|
void *output_value = params.get_output_data_ptr(0);
|
|
|
|
|
BLI_assert(input_value != nullptr);
|
|
|
|
|
BLI_assert(output_value != nullptr);
|
|
|
|
|
const CPPType &type = *inputs_[0].type;
|
|
|
|
|
type.move_construct(input_value, output_value);
|
|
|
|
|
params.output_set(0);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-16 12:54:46 -05:00
|
|
|
/**
|
|
|
|
|
* Lazy functions for nodes whose type cannot be found. An undefined function just outputs default
|
|
|
|
|
* values. It's useful to have so other parts of the conversion don't have to care about undefined
|
|
|
|
|
* nodes.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForUndefinedNode : public LazyFunction {
|
2023-12-17 14:00:07 +01:00
|
|
|
const bNode &node_;
|
|
|
|
|
|
2022-09-16 12:54:46 -05:00
|
|
|
public:
|
2023-04-22 16:14:38 +02:00
|
|
|
LazyFunctionForUndefinedNode(const bNode &node, MutableSpan<int> r_lf_index_by_bsocket)
|
2023-12-17 14:00:07 +01:00
|
|
|
: node_(node)
|
2022-09-16 12:54:46 -05:00
|
|
|
{
|
|
|
|
|
debug_name_ = "Undefined";
|
|
|
|
|
Vector<lf::Input> dummy_inputs;
|
2023-04-22 16:14:38 +02:00
|
|
|
lazy_function_interface_from_node(node, dummy_inputs, outputs_, r_lf_index_by_bsocket);
|
2022-09-16 12:54:46 -05:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
2022-09-16 12:54:46 -05:00
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
set_default_remaining_node_outputs(params, node_);
|
2022-09-16 12:54:46 -05:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-19 15:45:28 +01:00
|
|
|
static void set_default_value_for_output_socket(lf::Params ¶ms,
|
|
|
|
|
const int lf_index,
|
|
|
|
|
const bNodeSocket &bsocket)
|
|
|
|
|
{
|
|
|
|
|
const CPPType &cpp_type = *bsocket.typeinfo->geometry_nodes_cpp_type;
|
|
|
|
|
void *output_value = params.get_output_data_ptr(lf_index);
|
|
|
|
|
if (bsocket.typeinfo->geometry_nodes_default_cpp_value) {
|
|
|
|
|
cpp_type.copy_construct(bsocket.typeinfo->geometry_nodes_default_cpp_value, output_value);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
cpp_type.value_initialize(output_value);
|
|
|
|
|
}
|
|
|
|
|
params.output_set(lf_index);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-17 14:00:07 +01:00
|
|
|
void set_default_remaining_node_outputs(lf::Params ¶ms, const bNode &node)
|
|
|
|
|
{
|
|
|
|
|
const bNodeTree &ntree = node.owner_tree();
|
|
|
|
|
const Span<int> lf_index_by_bsocket =
|
|
|
|
|
ntree.runtime->geometry_nodes_lazy_function_graph_info->mapping.lf_index_by_bsocket;
|
|
|
|
|
for (const bNodeSocket *bsocket : node.output_sockets()) {
|
|
|
|
|
const int lf_index = lf_index_by_bsocket[bsocket->index_in_tree()];
|
|
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (params.output_was_set(lf_index)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-12-19 15:45:28 +01:00
|
|
|
set_default_value_for_output_socket(params, lf_index, *bsocket);
|
2023-12-17 14:00:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
/**
|
|
|
|
|
* Executes a multi-function. If all inputs are single values, the results will also be single
|
|
|
|
|
* values. If any input is a field, the outputs will also be fields.
|
|
|
|
|
*/
|
2023-12-17 14:00:07 +01:00
|
|
|
static void execute_multi_function_on_value_variant(const MultiFunction &fn,
|
|
|
|
|
const std::shared_ptr<MultiFunction> &owned_fn,
|
|
|
|
|
const Span<SocketValueVariant *> input_values,
|
|
|
|
|
const Span<SocketValueVariant *> output_values)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
/* Check if any input is a field. */
|
|
|
|
|
bool any_input_is_field = false;
|
2023-12-17 14:00:07 +01:00
|
|
|
for (const int i : input_values.index_range()) {
|
|
|
|
|
if (input_values[i]->is_context_dependent_field()) {
|
2022-09-13 08:44:26 +02:00
|
|
|
any_input_is_field = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (any_input_is_field) {
|
|
|
|
|
/* Convert all inputs into fields, so that they can be used as input in the new field. */
|
|
|
|
|
Vector<GField> input_fields;
|
2023-12-17 14:00:07 +01:00
|
|
|
for (const int i : input_values.index_range()) {
|
|
|
|
|
input_fields.append(input_values[i]->extract<GField>());
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Construct the new field node. */
|
|
|
|
|
std::shared_ptr<fn::FieldOperation> operation;
|
|
|
|
|
if (owned_fn) {
|
2023-04-23 14:03:50 -04:00
|
|
|
operation = fn::FieldOperation::Create(owned_fn, std::move(input_fields));
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2023-04-23 14:03:50 -04:00
|
|
|
operation = fn::FieldOperation::Create(fn, std::move(input_fields));
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Store the new fields in the output. */
|
2023-12-17 14:00:07 +01:00
|
|
|
for (const int i : output_values.index_range()) {
|
|
|
|
|
output_values[i]->set(GField{operation, i});
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* In this case, the multi-function is evaluated directly. */
|
BLI: refactor IndexMask for better performance and memory usage
Goals of this refactor:
* Reduce memory consumption of `IndexMask`. The old `IndexMask` uses an
`int64_t` for each index which is more than necessary in pretty much all
practical cases currently. Using `int32_t` might still become limiting
in the future in case we use this to index e.g. byte buffers larger than
a few gigabytes. We also don't want to template `IndexMask`, because
that would cause a split in the "ecosystem", or everything would have to
be implemented twice or templated.
* Allow for more multi-threading. The old `IndexMask` contains a single
array. This is generally good but has the problem that it is hard to fill
from multiple-threads when the final size is not known from the beginning.
This is commonly the case when e.g. converting an array of bool to an
index mask. Currently, this kind of code only runs on a single thread.
* Allow for efficient set operations like join, intersect and difference.
It should be possible to multi-thread those operations.
* It should be possible to iterate over an `IndexMask` very efficiently.
The most important part of that is to avoid all memory access when iterating
over continuous ranges. For some core nodes (e.g. math nodes), we generate
optimized code for the cases of irregular index masks and simple index ranges.
To achieve these goals, a few compromises had to made:
* Slicing of the mask (at specific indices) and random element access is
`O(log #indices)` now, but with a low constant factor. It should be possible
to split a mask into n approximately equally sized parts in `O(n)` though,
making the time per split `O(1)`.
* Using range-based for loops does not work well when iterating over a nested
data structure like the new `IndexMask`. Therefor, `foreach_*` functions with
callbacks have to be used. To avoid extra code complexity at the call site,
the `foreach_*` methods support multi-threading out of the box.
The new data structure splits an `IndexMask` into an arbitrary number of ordered
`IndexMaskSegment`. Each segment can contain at most `2^14 = 16384` indices. The
indices within a segment are stored as `int16_t`. Each segment has an additional
`int64_t` offset which allows storing arbitrary `int64_t` indices. This approach
has the main benefits that segments can be processed/constructed individually on
multiple threads without a serial bottleneck. Also it reduces the memory
requirements significantly.
For more details see comments in `BLI_index_mask.hh`.
I did a few tests to verify that the data structure generally improves
performance and does not cause regressions:
* Our field evaluation benchmarks take about as much as before. This is to be
expected because we already made sure that e.g. add node evaluation is
vectorized. The important thing here is to check that changes to the way we
iterate over the indices still allows for auto-vectorization.
* Memory usage by a mask is about 1/4 of what it was before in the average case.
That's mainly caused by the switch from `int64_t` to `int16_t` for indices.
In the worst case, the memory requirements can be larger when there are many
indices that are very far away. However, when they are far away from each other,
that indicates that there aren't many indices in total. In common cases, memory
usage can be way lower than 1/4 of before, because sub-ranges use static memory.
* For some more specific numbers I benchmarked `IndexMask::from_bools` in
`index_mask_from_selection` on 10.000.000 elements at various probabilities for
`true` at every index:
```
Probability Old New
0 4.6 ms 0.8 ms
0.001 5.1 ms 1.3 ms
0.2 8.4 ms 1.8 ms
0.5 15.3 ms 3.0 ms
0.8 20.1 ms 3.0 ms
0.999 25.1 ms 1.7 ms
1 13.5 ms 1.1 ms
```
Pull Request: https://projects.blender.org/blender/blender/pulls/104629
2023-05-24 18:11:41 +02:00
|
|
|
const IndexMask mask(1);
|
|
|
|
|
mf::ParamsBuilder params{fn, &mask};
|
2023-01-07 17:32:28 +01:00
|
|
|
mf::ContextBuilder context;
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-12-17 14:00:07 +01:00
|
|
|
for (const int i : input_values.index_range()) {
|
|
|
|
|
SocketValueVariant &input_variant = *input_values[i];
|
|
|
|
|
input_variant.convert_to_single();
|
2024-01-21 13:22:16 +01:00
|
|
|
const void *value = input_variant.get_single_ptr_raw();
|
|
|
|
|
const CPPType &cpp_type = fn.param_type(params.next_param_index()).data_type().single_type();
|
|
|
|
|
params.add_readonly_single_input(GPointer{cpp_type, value});
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2023-12-17 14:00:07 +01:00
|
|
|
for (const int i : output_values.index_range()) {
|
|
|
|
|
SocketValueVariant &output_variant = *output_values[i];
|
|
|
|
|
const CPPType &cpp_type = fn.param_type(params.next_param_index()).data_type().single_type();
|
2024-01-21 13:22:16 +01:00
|
|
|
const eNodeSocketDatatype socket_type =
|
|
|
|
|
bke::geo_nodes_base_cpp_type_to_socket_type(cpp_type).value();
|
|
|
|
|
void *value = output_variant.allocate_single(socket_type);
|
2023-12-17 14:00:07 +01:00
|
|
|
params.add_uninitialized_single_output(GMutableSpan{cpp_type, value, 1});
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
BLI: refactor IndexMask for better performance and memory usage
Goals of this refactor:
* Reduce memory consumption of `IndexMask`. The old `IndexMask` uses an
`int64_t` for each index which is more than necessary in pretty much all
practical cases currently. Using `int32_t` might still become limiting
in the future in case we use this to index e.g. byte buffers larger than
a few gigabytes. We also don't want to template `IndexMask`, because
that would cause a split in the "ecosystem", or everything would have to
be implemented twice or templated.
* Allow for more multi-threading. The old `IndexMask` contains a single
array. This is generally good but has the problem that it is hard to fill
from multiple-threads when the final size is not known from the beginning.
This is commonly the case when e.g. converting an array of bool to an
index mask. Currently, this kind of code only runs on a single thread.
* Allow for efficient set operations like join, intersect and difference.
It should be possible to multi-thread those operations.
* It should be possible to iterate over an `IndexMask` very efficiently.
The most important part of that is to avoid all memory access when iterating
over continuous ranges. For some core nodes (e.g. math nodes), we generate
optimized code for the cases of irregular index masks and simple index ranges.
To achieve these goals, a few compromises had to made:
* Slicing of the mask (at specific indices) and random element access is
`O(log #indices)` now, but with a low constant factor. It should be possible
to split a mask into n approximately equally sized parts in `O(n)` though,
making the time per split `O(1)`.
* Using range-based for loops does not work well when iterating over a nested
data structure like the new `IndexMask`. Therefor, `foreach_*` functions with
callbacks have to be used. To avoid extra code complexity at the call site,
the `foreach_*` methods support multi-threading out of the box.
The new data structure splits an `IndexMask` into an arbitrary number of ordered
`IndexMaskSegment`. Each segment can contain at most `2^14 = 16384` indices. The
indices within a segment are stored as `int16_t`. Each segment has an additional
`int64_t` offset which allows storing arbitrary `int64_t` indices. This approach
has the main benefits that segments can be processed/constructed individually on
multiple threads without a serial bottleneck. Also it reduces the memory
requirements significantly.
For more details see comments in `BLI_index_mask.hh`.
I did a few tests to verify that the data structure generally improves
performance and does not cause regressions:
* Our field evaluation benchmarks take about as much as before. This is to be
expected because we already made sure that e.g. add node evaluation is
vectorized. The important thing here is to check that changes to the way we
iterate over the indices still allows for auto-vectorization.
* Memory usage by a mask is about 1/4 of what it was before in the average case.
That's mainly caused by the switch from `int64_t` to `int16_t` for indices.
In the worst case, the memory requirements can be larger when there are many
indices that are very far away. However, when they are far away from each other,
that indicates that there aren't many indices in total. In common cases, memory
usage can be way lower than 1/4 of before, because sub-ranges use static memory.
* For some more specific numbers I benchmarked `IndexMask::from_bools` in
`index_mask_from_selection` on 10.000.000 elements at various probabilities for
`true` at every index:
```
Probability Old New
0 4.6 ms 0.8 ms
0.001 5.1 ms 1.3 ms
0.2 8.4 ms 1.8 ms
0.5 15.3 ms 3.0 ms
0.8 20.1 ms 3.0 ms
0.999 25.1 ms 1.7 ms
1 13.5 ms 1.1 ms
```
Pull Request: https://projects.blender.org/blender/blender/pulls/104629
2023-05-24 18:11:41 +02:00
|
|
|
fn.call(mask, params, context);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Behavior of muted nodes:
|
|
|
|
|
* - Some inputs are forwarded to outputs without changes.
|
|
|
|
|
* - Some inputs are converted to a different type which becomes the output.
|
|
|
|
|
* - Some outputs are value initialized because they don't have a corresponding input.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForMutedNode : public LazyFunction {
|
|
|
|
|
private:
|
2023-12-19 15:45:28 +01:00
|
|
|
const bNode &node_;
|
|
|
|
|
Span<int> lf_index_by_bsocket_;
|
|
|
|
|
Array<const bNodeSocket *> input_by_output_index_;
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
public:
|
2023-04-22 16:14:38 +02:00
|
|
|
LazyFunctionForMutedNode(const bNode &node, MutableSpan<int> r_lf_index_by_bsocket)
|
2023-12-19 15:45:28 +01:00
|
|
|
: node_(node), lf_index_by_bsocket_(r_lf_index_by_bsocket)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
debug_name_ = "Muted";
|
2023-04-22 16:14:38 +02:00
|
|
|
lazy_function_interface_from_node(node, inputs_, outputs_, r_lf_index_by_bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
for (lf::Input &fn_input : inputs_) {
|
|
|
|
|
fn_input.usage = lf::ValueUsage::Maybe;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (lf::Input &fn_input : inputs_) {
|
|
|
|
|
fn_input.usage = lf::ValueUsage::Unused;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-19 14:52:43 +01:00
|
|
|
input_by_output_index_.reinitialize(node.output_sockets().size());
|
2023-12-19 15:45:28 +01:00
|
|
|
input_by_output_index_.fill(nullptr);
|
2023-01-09 23:29:58 -05:00
|
|
|
for (const bNodeLink &internal_link : node.internal_links()) {
|
2023-04-22 16:14:38 +02:00
|
|
|
const int input_i = r_lf_index_by_bsocket[internal_link.fromsock->index_in_tree()];
|
|
|
|
|
const int output_i = r_lf_index_by_bsocket[internal_link.tosock->index_in_tree()];
|
2022-09-13 08:44:26 +02:00
|
|
|
if (ELEM(-1, input_i, output_i)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-12-19 15:45:28 +01:00
|
|
|
input_by_output_index_[internal_link.tosock->index()] = internal_link.fromsock;
|
2022-09-13 08:44:26 +02:00
|
|
|
inputs_[input_i].usage = lf::ValueUsage::Maybe;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
2023-12-19 15:45:28 +01:00
|
|
|
for (const bNodeSocket *output_bsocket : node_.output_sockets()) {
|
|
|
|
|
const int lf_output_index = lf_index_by_bsocket_[output_bsocket->index_in_tree()];
|
|
|
|
|
if (lf_output_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (params.output_was_set(lf_output_index)) {
|
2022-09-13 08:44:26 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2023-12-19 15:45:28 +01:00
|
|
|
if (params.get_output_usage(lf_output_index) != lf::ValueUsage::Used) {
|
2023-04-17 10:58:13 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2023-12-19 15:45:28 +01:00
|
|
|
const bNodeSocket *input_bsocket = input_by_output_index_[output_bsocket->index()];
|
|
|
|
|
if (input_bsocket == nullptr) {
|
|
|
|
|
set_default_value_for_output_socket(params, lf_output_index, *output_bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2023-12-19 15:45:28 +01:00
|
|
|
const int lf_input_index = lf_index_by_bsocket_[input_bsocket->index_in_tree()];
|
|
|
|
|
const void *input_value = params.try_get_input_data_ptr_or_request(lf_input_index);
|
2022-09-13 08:44:26 +02:00
|
|
|
if (input_value == nullptr) {
|
2023-12-19 15:45:28 +01:00
|
|
|
/* Wait for value to be available. */
|
2022-09-13 08:44:26 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2023-12-19 15:45:28 +01:00
|
|
|
void *output_value = params.get_output_data_ptr(lf_output_index);
|
|
|
|
|
if (input_bsocket->type == output_bsocket->type) {
|
|
|
|
|
inputs_[lf_input_index].type->copy_construct(input_value, output_value);
|
|
|
|
|
params.output_set(lf_output_index);
|
2022-09-13 08:44:26 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
|
2023-12-19 15:45:28 +01:00
|
|
|
if (conversions.is_convertible(*input_bsocket->typeinfo->base_cpp_type,
|
|
|
|
|
*output_bsocket->typeinfo->base_cpp_type))
|
|
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
const MultiFunction &multi_fn = *conversions.get_conversion_multi_function(
|
2023-12-19 15:45:28 +01:00
|
|
|
mf::DataType::ForSingle(*input_bsocket->typeinfo->base_cpp_type),
|
|
|
|
|
mf::DataType::ForSingle(*output_bsocket->typeinfo->base_cpp_type));
|
|
|
|
|
SocketValueVariant input_variant = *static_cast<const SocketValueVariant *>(input_value);
|
|
|
|
|
SocketValueVariant *output_variant = new (output_value) SocketValueVariant();
|
|
|
|
|
execute_multi_function_on_value_variant(multi_fn, {}, {&input_variant}, {output_variant});
|
|
|
|
|
params.output_set(lf_output_index);
|
|
|
|
|
continue;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2023-12-19 15:45:28 +01:00
|
|
|
set_default_value_for_output_socket(params, lf_output_index, *output_bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Type conversions are generally implemented as multi-functions. This node checks if the input is
|
|
|
|
|
* a field or single value and outputs a field or single value respectively.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForMultiFunctionConversion : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const MultiFunction &fn_;
|
|
|
|
|
|
|
|
|
|
public:
|
2023-12-17 14:00:07 +01:00
|
|
|
LazyFunctionForMultiFunctionConversion(const MultiFunction &fn) : fn_(fn)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
debug_name_ = "Convert";
|
2023-12-17 14:00:07 +01:00
|
|
|
inputs_.append_as("From", CPPType::get<SocketValueVariant>());
|
|
|
|
|
outputs_.append_as("To", CPPType::get<SocketValueVariant>());
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
SocketValueVariant *from_value = params.try_get_input_data_ptr<SocketValueVariant>(0);
|
|
|
|
|
SocketValueVariant *to_value = new (params.get_output_data_ptr(0)) SocketValueVariant();
|
2022-09-13 08:44:26 +02:00
|
|
|
BLI_assert(from_value != nullptr);
|
|
|
|
|
BLI_assert(to_value != nullptr);
|
|
|
|
|
|
2023-12-17 14:00:07 +01:00
|
|
|
execute_multi_function_on_value_variant(fn_, {}, {from_value}, {to_value});
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
params.output_set(0);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This lazy-function wraps nodes that are implemented as multi-function (mostly math nodes).
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForMultiFunctionNode : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const NodeMultiFunctions::Item fn_item_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForMultiFunctionNode(const bNode &node,
|
|
|
|
|
NodeMultiFunctions::Item fn_item,
|
2023-04-22 16:14:38 +02:00
|
|
|
MutableSpan<int> r_lf_index_by_bsocket)
|
2022-09-14 12:02:27 +02:00
|
|
|
: fn_item_(std::move(fn_item))
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
BLI_assert(fn_item_.fn != nullptr);
|
|
|
|
|
debug_name_ = node.name;
|
2023-04-22 16:14:38 +02:00
|
|
|
lazy_function_interface_from_node(node, inputs_, outputs_, r_lf_index_by_bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
Vector<SocketValueVariant *> input_values(inputs_.size());
|
|
|
|
|
Vector<SocketValueVariant *> output_values(outputs_.size());
|
2022-09-13 08:44:26 +02:00
|
|
|
for (const int i : inputs_.index_range()) {
|
2023-12-17 14:00:07 +01:00
|
|
|
input_values[i] = params.try_get_input_data_ptr<SocketValueVariant>(i);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
for (const int i : outputs_.index_range()) {
|
2023-12-17 14:00:07 +01:00
|
|
|
output_values[i] = new (params.get_output_data_ptr(i)) SocketValueVariant();
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2023-12-13 13:40:40 +01:00
|
|
|
execute_multi_function_on_value_variant(
|
2023-12-17 14:00:07 +01:00
|
|
|
*fn_item_.fn, fn_item_.owned_fn, input_values, output_values);
|
2022-09-13 08:44:26 +02:00
|
|
|
for (const int i : outputs_.index_range()) {
|
|
|
|
|
params.output_set(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Some sockets have non-trivial implicit inputs (e.g. the Position input of the Set Position
|
|
|
|
|
* node). Those are implemented as a separate node that outputs the value.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForImplicitInput : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
/**
|
|
|
|
|
* The function that generates the implicit input. The passed in memory is uninitialized.
|
|
|
|
|
*/
|
|
|
|
|
std::function<void(void *)> init_fn_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForImplicitInput(const CPPType &type, std::function<void(void *)> init_fn)
|
|
|
|
|
: init_fn_(std::move(init_fn))
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Input";
|
|
|
|
|
outputs_.append({"Output", type});
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
void *value = params.get_output_data_ptr(0);
|
|
|
|
|
init_fn_(value);
|
|
|
|
|
params.output_set(0);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The viewer node does not have outputs. Instead it is executed because the executor knows that it
|
|
|
|
|
* has side effects. The side effect is that the inputs to the viewer are logged.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForViewerNode : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const bNode &bnode_;
|
|
|
|
|
/** The field is only logged when it is linked. */
|
|
|
|
|
bool use_field_input_ = true;
|
|
|
|
|
|
|
|
|
|
public:
|
2023-04-22 16:14:38 +02:00
|
|
|
LazyFunctionForViewerNode(const bNode &bnode, MutableSpan<int> r_lf_index_by_bsocket)
|
2022-09-13 08:44:26 +02:00
|
|
|
: bnode_(bnode)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Viewer";
|
2023-04-22 16:14:38 +02:00
|
|
|
lazy_function_interface_from_node(bnode, inputs_, outputs_, r_lf_index_by_bsocket);
|
|
|
|
|
|
|
|
|
|
/* Remove field input if it is not used. */
|
|
|
|
|
for (const bNodeSocket *bsocket : bnode.input_sockets().drop_front(1)) {
|
|
|
|
|
if (!bsocket->is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const Span<const bNodeLink *> links = bsocket->directly_linked_links();
|
2023-05-16 10:15:56 +12:00
|
|
|
if (links.is_empty() ||
|
2024-01-02 18:12:54 +01:00
|
|
|
bke::nodeIsDanglingReroute(&bnode.owner_tree(), links.first()->fromnode))
|
|
|
|
|
{
|
2023-04-22 16:14:38 +02:00
|
|
|
use_field_input_ = false;
|
|
|
|
|
inputs_.pop_last();
|
|
|
|
|
r_lf_index_by_bsocket[bsocket->index_in_tree()] = -1;
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
|
|
|
|
{
|
2023-09-21 14:35:20 +02:00
|
|
|
const auto &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
2023-05-09 13:10:47 +02:00
|
|
|
const auto &local_user_data = *static_cast<GeoNodesLFLocalUserData *>(context.local_user_data);
|
2023-09-21 14:35:20 +02:00
|
|
|
geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(user_data);
|
2023-09-16 10:07:27 +02:00
|
|
|
if (tree_logger == nullptr) {
|
2022-09-25 23:07:21 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
GeometrySet geometry = params.extract_input<GeometrySet>(0);
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
const NodeGeometryViewer *storage = static_cast<NodeGeometryViewer *>(bnode_.storage);
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
if (use_field_input_) {
|
2023-12-17 14:00:07 +01:00
|
|
|
SocketValueVariant *value_variant = params.try_get_input_data_ptr<SocketValueVariant>(1);
|
2023-12-13 13:40:40 +01:00
|
|
|
BLI_assert(value_variant != nullptr);
|
2023-12-17 14:00:07 +01:00
|
|
|
GField field = value_variant->extract<GField>();
|
2023-12-20 13:13:16 -05:00
|
|
|
const AttrDomain domain = AttrDomain(storage->domain);
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
const StringRefNull viewer_attribute_name = ".viewer";
|
2023-12-20 13:13:16 -05:00
|
|
|
if (domain == AttrDomain::Instance) {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
if (geometry.has_instances()) {
|
|
|
|
|
GeometryComponent &component = geometry.get_component_for_write(
|
2023-06-15 22:18:28 +02:00
|
|
|
bke::GeometryComponent::Type::Instance);
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
bke::try_capture_field_on_geometry(
|
2023-12-20 13:13:16 -05:00
|
|
|
component, viewer_attribute_name, AttrDomain::Instance, field);
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
|
2023-06-15 22:18:28 +02:00
|
|
|
for (const bke::GeometryComponent::Type type : {bke::GeometryComponent::Type::Mesh,
|
|
|
|
|
bke::GeometryComponent::Type::PointCloud,
|
|
|
|
|
bke::GeometryComponent::Type::Curve})
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
{
|
|
|
|
|
if (geometry.has(type)) {
|
|
|
|
|
GeometryComponent &component = geometry.get_component_for_write(type);
|
2023-12-20 13:13:16 -05:00
|
|
|
AttrDomain used_domain = domain;
|
|
|
|
|
if (used_domain == AttrDomain::Auto) {
|
2023-12-20 14:54:38 -05:00
|
|
|
if (const std::optional<AttrDomain> detected_domain = bke::try_detect_field_domain(
|
|
|
|
|
component, field))
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
{
|
|
|
|
|
used_domain = *detected_domain;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-12-20 13:13:16 -05:00
|
|
|
used_domain = AttrDomain::Point;
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bke::try_capture_field_on_geometry(
|
|
|
|
|
component, viewer_attribute_name, used_domain, field);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-16 10:07:27 +02:00
|
|
|
tree_logger->log_viewer_node(bnode_, std::move(geometry));
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
/**
|
|
|
|
|
* Outputs true when a specific viewer node is used in the current context and false otherwise.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForViewerInputUsage : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const lf::FunctionNode &lf_viewer_node_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForViewerInputUsage(const lf::FunctionNode &lf_viewer_node)
|
|
|
|
|
: lf_viewer_node_(lf_viewer_node)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Viewer Input Usage";
|
|
|
|
|
outputs_.append_as("Viewer is Used", CPPType::get<bool>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
BLI_assert(user_data != nullptr);
|
2023-11-29 13:22:20 +01:00
|
|
|
if (!user_data->call_data->side_effect_nodes) {
|
2023-12-17 14:00:07 +01:00
|
|
|
params.set_output<bool>(0, false);
|
2023-08-04 18:22:45 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
const ComputeContextHash &context_hash = user_data->compute_context->hash();
|
|
|
|
|
const Span<const lf::FunctionNode *> nodes_with_side_effects =
|
2023-11-29 13:22:20 +01:00
|
|
|
user_data->call_data->side_effect_nodes->nodes_by_context.lookup(context_hash);
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
const bool viewer_is_used = nodes_with_side_effects.contains(&lf_viewer_node_);
|
|
|
|
|
params.set_output(0, viewer_is_used);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
class LazyFunctionForSimulationInputsUsage : public LazyFunction {
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
private:
|
|
|
|
|
const bNode *output_bnode_;
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
|
|
|
|
|
public:
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
LazyFunctionForSimulationInputsUsage(const bNode &output_bnode) : output_bnode_(&output_bnode)
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
{
|
|
|
|
|
debug_name_ = "Simulation Inputs Usage";
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
outputs_.append_as("Need Input Inputs", CPPType::get<bool>());
|
|
|
|
|
outputs_.append_as("Need Output Inputs", CPPType::get<bool>());
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
const GeoNodesLFUserData &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
2023-11-29 13:22:20 +01:00
|
|
|
const GeoNodesCallData &call_data = *user_data.call_data;
|
|
|
|
|
if (!call_data.simulation_params) {
|
2023-12-17 14:00:07 +01:00
|
|
|
this->set_default_outputs(params);
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const std::optional<FoundNestedNodeID> found_id = find_nested_node_id(
|
|
|
|
|
user_data, output_bnode_->identifier);
|
|
|
|
|
if (!found_id) {
|
2023-12-17 14:00:07 +01:00
|
|
|
this->set_default_outputs(params);
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (found_id->is_in_loop) {
|
2023-12-17 14:00:07 +01:00
|
|
|
this->set_default_outputs(params);
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2023-11-29 13:22:20 +01:00
|
|
|
SimulationZoneBehavior *zone_behavior = call_data.simulation_params->get(found_id->id);
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
if (!zone_behavior) {
|
2023-12-17 14:00:07 +01:00
|
|
|
this->set_default_outputs(params);
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool solve_contains_side_effect = false;
|
2023-11-29 13:22:20 +01:00
|
|
|
if (call_data.side_effect_nodes) {
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
const Span<const lf::FunctionNode *> side_effect_nodes =
|
2023-11-29 13:22:20 +01:00
|
|
|
call_data.side_effect_nodes->nodes_by_context.lookup(user_data.compute_context->hash());
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
solve_contains_side_effect = !side_effect_nodes.is_empty();
|
|
|
|
|
}
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
params.set_output(0, std::holds_alternative<sim_input::PassThrough>(zone_behavior->input));
|
|
|
|
|
params.set_output(
|
|
|
|
|
1,
|
|
|
|
|
solve_contains_side_effect ||
|
2023-09-09 09:53:01 +02:00
|
|
|
std::holds_alternative<sim_output::StoreNewState>(zone_behavior->output));
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
2023-12-17 14:00:07 +01:00
|
|
|
|
|
|
|
|
void set_default_outputs(lf::Params ¶ms) const
|
|
|
|
|
{
|
|
|
|
|
params.set_output(0, false);
|
|
|
|
|
params.set_output(1, false);
|
|
|
|
|
}
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
};
|
|
|
|
|
|
2023-12-18 13:01:06 +01:00
|
|
|
class LazyFunctionForBakeInputsUsage : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const bNode *bnode_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForBakeInputsUsage(const bNode &bnode) : bnode_(&bnode)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Bake Inputs Usage";
|
|
|
|
|
outputs_.append_as("Used", CPPType::get<bool>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
const GeoNodesLFUserData &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
if (!user_data.call_data->bake_params) {
|
|
|
|
|
this->set_default_outputs(params);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const std::optional<FoundNestedNodeID> found_id = find_nested_node_id(user_data,
|
|
|
|
|
bnode_->identifier);
|
|
|
|
|
if (!found_id) {
|
|
|
|
|
this->set_default_outputs(params);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (found_id->is_in_loop || found_id->is_in_simulation) {
|
|
|
|
|
this->set_default_outputs(params);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
BakeNodeBehavior *behavior = user_data.call_data->bake_params->get(found_id->id);
|
|
|
|
|
if (!behavior) {
|
|
|
|
|
this->set_default_outputs(params);
|
|
|
|
|
return;
|
|
|
|
|
}
|
Geometry Nodes: support baking data block references
With this patch, materials are kept intact in simulation zones and bake nodes
without any additional user action.
This implements the design proposed in #108410 to support referencing
data-blocks (only materials for now) in the baked data. The task also describes
why this is not a trivial issue. A previous attempt was implemented in #109703
but it didn't work well-enough.
The solution is to have an explicit `name (+ library name) -> data-block`
mapping that is stored in the modifier for each bake node and simulation zone.
The `library name` is necessary for it to be unique within a .blend file. Note
that this refers to the name of the `Library` data-block and not a file path.
The baked data only contains the names of the used data-blocks. When the baked
data is loaded, the correct material data-block is looked up from the mapping.
### Automatic Mapping Generation
The most tricky aspect of this approach is to make it feel mostly automatic.
From the user point-of-view, it should just work. Therefore, we don't want the
user to have to create the mapping manually in the majority of cases. Creating
the mapping automatically is difficult because the data-blocks that should
become part of the mapping are only known during depsgraph evaluation. So we
somehow have to gather the missing data blocks during evaluation and then write
the new mappings back to the original data.
While writing back to original data is something we do in some cases already,
the situation here is different, because we are actually creating new relations
between data-blocks. This also means that we'll have to do user-counting. Since
user counts in data-blocks are *not* atomic, we can't do that from multiple
threads at the same time. Also, under some circumstances, it may be necessary to
trigger depsgraph evaluation again after the write-back because it actually
affects the result.
To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It
allows gathering tasks which write back to original data in a synchronous way
which may also require a reevaluation.
### Accessing the Mapping
A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier.
This map allows getting the `ID` pointer that should be used for a specific
data-block name that is stored in baked data. It's also used to gather all the
missing data mappings during evaluation.
### Weak ID References
The baked/cached geometries may have references to other data-blocks (currently
only materials, but in the future also e.g. instanced objects/collections).
However, the pointers of these data-blocks are not stable over time. That is
especially true when storing/loading the data from disk, but also just when
playing back the animation. Therefore, the used data-blocks have to referenced
in a different way at run-time.
This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the
run-time data of various geometry data-blocks. If the data-block is cached over
a longer period of time (such that material pointers can't be used directly), it
stores the material name (+ library name) used by each material slot. When the
geometry is used again, the material pointers are restored using these weak name
references and the `BakeDataBlockMap`.
### Manual Mapping Management
There is a new `Data-Blocks` panel in the bake settings in the node editor
sidebar that allows inspecting and modifying the data-blocks that are used when
baking. The user can change what data-block a specific name is mapped to.
Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
|
|
|
const bool need_inputs = std::holds_alternative<sim_output::PassThrough>(behavior->behavior) ||
|
|
|
|
|
std::holds_alternative<sim_output::StoreNewState>(behavior->behavior);
|
2023-12-18 13:01:06 +01:00
|
|
|
params.set_output(0, need_inputs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_default_outputs(lf::Params ¶ms) const
|
|
|
|
|
{
|
|
|
|
|
params.set_output(0, false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-11-21 14:27:21 +01:00
|
|
|
static bool should_log_socket_values_for_context(const GeoNodesLFUserData &user_data,
|
|
|
|
|
const ComputeContextHash hash)
|
|
|
|
|
{
|
2023-11-29 13:22:20 +01:00
|
|
|
if (const Set<ComputeContextHash> *contexts = user_data.call_data->socket_log_contexts) {
|
|
|
|
|
return contexts->contains(hash);
|
2023-11-21 14:27:21 +01:00
|
|
|
}
|
2023-11-29 13:22:20 +01:00
|
|
|
else if (user_data.call_data->operator_data) {
|
2023-11-21 14:27:21 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
/**
|
|
|
|
|
* This lazy-function wraps a group node. Internally it just executes the lazy-function graph of
|
|
|
|
|
* the referenced group.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForGroupNode : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const bNode &group_node_;
|
2023-09-17 19:09:45 +02:00
|
|
|
const LazyFunction &group_lazy_function_;
|
2022-09-20 10:59:12 +02:00
|
|
|
bool has_many_nodes_ = false;
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2022-12-29 20:45:51 +01:00
|
|
|
struct Storage {
|
2023-09-17 19:09:45 +02:00
|
|
|
void *group_storage = nullptr;
|
2022-12-29 20:45:51 +01:00
|
|
|
/* To avoid computing the hash more than once. */
|
|
|
|
|
std::optional<ComputeContextHash> context_hash_cache;
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
public:
|
|
|
|
|
LazyFunctionForGroupNode(const bNode &group_node,
|
2023-04-26 10:42:23 +02:00
|
|
|
const GeometryNodesLazyFunctionGraphInfo &group_lf_graph_info,
|
|
|
|
|
GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
|
2023-09-17 19:09:45 +02:00
|
|
|
: group_node_(group_node), group_lazy_function_(*group_lf_graph_info.function.function)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
debug_name_ = group_node.name;
|
2023-01-05 14:05:30 +01:00
|
|
|
allow_missing_requested_inputs_ = true;
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-09-17 19:09:45 +02:00
|
|
|
/* This wrapper has the same interface as the actual underlying node group. */
|
|
|
|
|
inputs_ = group_lf_graph_info.function.function->inputs();
|
|
|
|
|
outputs_ = group_lf_graph_info.function.function->outputs();
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-04-26 10:42:23 +02:00
|
|
|
has_many_nodes_ = group_lf_graph_info.num_inline_nodes_approximate > 1000;
|
2022-09-20 10:59:12 +02:00
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
/* Add a boolean input for every output bsocket that indicates whether that socket is used. */
|
|
|
|
|
for (const int i : group_node.output_sockets().index_range()) {
|
2023-04-26 10:42:23 +02:00
|
|
|
own_lf_graph_info.mapping.lf_input_index_for_output_bsocket_usage
|
2023-09-17 19:09:45 +02:00
|
|
|
[group_node.output_socket(i).index_in_all_outputs()] =
|
|
|
|
|
group_lf_graph_info.function.inputs.output_usages[i];
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add an attribute set input for every output geometry socket that can propagate attributes
|
|
|
|
|
* from inputs. */
|
2023-09-17 19:09:45 +02:00
|
|
|
for (const int i : group_lf_graph_info.function.inputs.attributes_to_propagate.geometry_outputs
|
|
|
|
|
.index_range())
|
2023-04-26 10:42:23 +02:00
|
|
|
{
|
2023-09-17 19:09:45 +02:00
|
|
|
const int lf_index = group_lf_graph_info.function.inputs.attributes_to_propagate.range[i];
|
|
|
|
|
const int output_index =
|
|
|
|
|
group_lf_graph_info.function.inputs.attributes_to_propagate.geometry_outputs[i];
|
|
|
|
|
const bNodeSocket &output_bsocket = group_node_.output_socket(output_index);
|
2023-04-26 10:42:23 +02:00
|
|
|
own_lf_graph_info.mapping.lf_input_index_for_attribute_propagation_to_output
|
2023-09-17 19:09:45 +02:00
|
|
|
[output_bsocket.index_in_all_outputs()] = lf_index;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
BLI_assert(user_data != nullptr);
|
|
|
|
|
|
2022-09-20 10:59:12 +02:00
|
|
|
if (has_many_nodes_) {
|
|
|
|
|
/* If the called node group has many nodes, it's likely that executing it takes a while even
|
|
|
|
|
* if every individual node is very small. */
|
|
|
|
|
lazy_threading::send_hint();
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-29 20:45:51 +01:00
|
|
|
Storage *storage = static_cast<Storage *>(context.storage);
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
/* The compute context changes when entering a node group. */
|
2023-11-21 14:12:07 +01:00
|
|
|
bke::GroupNodeComputeContext compute_context{
|
2022-12-29 20:45:51 +01:00
|
|
|
user_data->compute_context, group_node_.identifier, storage->context_hash_cache};
|
|
|
|
|
storage->context_hash_cache = compute_context.hash();
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
GeoNodesLFUserData group_user_data = *user_data;
|
|
|
|
|
group_user_data.compute_context = &compute_context;
|
2023-11-21 14:27:21 +01:00
|
|
|
group_user_data.log_socket_values = should_log_socket_values_for_context(
|
|
|
|
|
*user_data, compute_context.hash());
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-05-09 13:10:47 +02:00
|
|
|
GeoNodesLFLocalUserData group_local_user_data{group_user_data};
|
2023-09-17 19:09:45 +02:00
|
|
|
lf::Context group_context{storage->group_storage, &group_user_data, &group_local_user_data};
|
|
|
|
|
group_lazy_function_.execute(params, group_context);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-14 12:02:27 +02:00
|
|
|
void *init_storage(LinearAllocator<> &allocator) const override
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
2022-12-29 20:45:51 +01:00
|
|
|
Storage *s = allocator.construct<Storage>().release();
|
2023-09-17 19:09:45 +02:00
|
|
|
s->group_storage = group_lazy_function_.init_storage(allocator);
|
2022-12-29 20:45:51 +01:00
|
|
|
return s;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-14 12:02:27 +02:00
|
|
|
void destruct_storage(void *storage) const override
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
2022-12-29 20:45:51 +01:00
|
|
|
Storage *s = static_cast<Storage *>(storage);
|
2023-09-17 19:09:45 +02:00
|
|
|
group_lazy_function_.destruct_storage(s->group_storage);
|
2022-12-29 20:45:51 +01:00
|
|
|
std::destroy_at(s);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
std::string name() const override
|
|
|
|
|
{
|
2022-09-17 01:50:55 +02:00
|
|
|
return fmt::format(TIP_("Group '{}' ({})"), group_node_.id->name + 2, group_node_.name);
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string input_name(const int i) const override
|
|
|
|
|
{
|
2023-09-17 19:09:45 +02:00
|
|
|
return group_lazy_function_.input_name(i);
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string output_name(const int i) const override
|
|
|
|
|
{
|
2023-09-17 19:09:45 +02:00
|
|
|
return group_lazy_function_.output_name(i);
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator,
|
|
|
|
|
const bNodeSocket &bsocket)
|
|
|
|
|
{
|
|
|
|
|
const bNodeSocketType &typeinfo = *bsocket.typeinfo;
|
|
|
|
|
const CPPType *type = get_socket_cpp_type(typeinfo);
|
|
|
|
|
if (type == nullptr) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
void *buffer = allocator.allocate(type->size(), type->alignment());
|
2023-08-30 12:37:21 +02:00
|
|
|
typeinfo.get_geometry_nodes_cpp_value(bsocket.default_value, buffer);
|
2022-09-13 08:44:26 +02:00
|
|
|
return {type, buffer};
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
/**
|
|
|
|
|
* Computes the logical or of the inputs and supports short-circuit evaluation (i.e. if the first
|
|
|
|
|
* input is true already, the other inputs are not checked).
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForLogicalOr : public lf::LazyFunction {
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForLogicalOr(const int inputs_num)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Logical Or";
|
|
|
|
|
for ([[maybe_unused]] const int i : IndexRange(inputs_num)) {
|
|
|
|
|
inputs_.append_as("Input", CPPType::get<bool>(), lf::ValueUsage::Maybe);
|
|
|
|
|
}
|
|
|
|
|
outputs_.append_as("Output", CPPType::get<bool>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
|
|
|
|
{
|
|
|
|
|
int first_unavailable_input = -1;
|
|
|
|
|
for (const int i : inputs_.index_range()) {
|
|
|
|
|
if (const bool *value = params.try_get_input_data_ptr<bool>(i)) {
|
|
|
|
|
if (*value) {
|
|
|
|
|
params.set_output(0, true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
first_unavailable_input = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (first_unavailable_input == -1) {
|
|
|
|
|
params.set_output(0, false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
params.try_get_input_data_ptr_or_request(first_unavailable_input);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Outputs booleans that indicate which inputs of a switch node are used. Note that it's possible
|
|
|
|
|
* that both inputs are used when the condition is a field.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForSwitchSocketUsage : public lf::LazyFunction {
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForSwitchSocketUsage()
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Switch Socket Usage";
|
2023-12-17 14:00:07 +01:00
|
|
|
inputs_.append_as("Condition", CPPType::get<SocketValueVariant>());
|
2023-01-05 14:05:30 +01:00
|
|
|
outputs_.append_as("False", CPPType::get<bool>());
|
|
|
|
|
outputs_.append_as("True", CPPType::get<bool>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
|
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
const SocketValueVariant &condition_variant = params.get_input<SocketValueVariant>(0);
|
|
|
|
|
if (condition_variant.is_context_dependent_field()) {
|
2023-01-05 14:05:30 +01:00
|
|
|
params.set_output(0, true);
|
|
|
|
|
params.set_output(1, true);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-12-17 14:00:07 +01:00
|
|
|
const bool value = condition_variant.get<bool>();
|
2023-01-05 14:05:30 +01:00
|
|
|
params.set_output(0, !value);
|
|
|
|
|
params.set_output(1, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-11-22 16:11:32 +01:00
|
|
|
/**
|
|
|
|
|
* Outputs booleans that indicate which inputs of a switch node are used. Note that it's possible
|
|
|
|
|
* that all inputs are used when the index input is a field.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForIndexSwitchSocketUsage : public lf::LazyFunction {
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForIndexSwitchSocketUsage(const bNode &bnode)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Index Switch Socket Usage";
|
2023-12-17 14:00:07 +01:00
|
|
|
inputs_.append_as("Index", CPPType::get<SocketValueVariant>());
|
2023-11-22 16:11:32 +01:00
|
|
|
for (const bNodeSocket *socket : bnode.input_sockets().drop_front(1)) {
|
|
|
|
|
outputs_.append_as(socket->identifier, CPPType::get<bool>());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
|
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
const SocketValueVariant &index_variant = params.get_input<SocketValueVariant>(0);
|
|
|
|
|
if (index_variant.is_context_dependent_field()) {
|
2023-11-22 16:11:32 +01:00
|
|
|
for (const int i : outputs_.index_range()) {
|
|
|
|
|
params.set_output(i, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-12-17 14:00:07 +01:00
|
|
|
const int value = index_variant.get<bool>();
|
2023-11-22 16:11:32 +01:00
|
|
|
for (const int i : outputs_.index_range()) {
|
|
|
|
|
params.set_output(i, i == value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
/**
|
|
|
|
|
* Takes a field as input and extracts the set of anonymous attributes that it references.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForAnonymousAttributeSetExtract : public lf::LazyFunction {
|
|
|
|
|
public:
|
2023-12-17 14:00:07 +01:00
|
|
|
LazyFunctionForAnonymousAttributeSetExtract()
|
2023-01-05 14:05:30 +01:00
|
|
|
{
|
|
|
|
|
debug_name_ = "Extract Attribute Set";
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
inputs_.append_as("Use", CPPType::get<bool>());
|
2023-12-17 14:00:07 +01:00
|
|
|
inputs_.append_as("Field", CPPType::get<SocketValueVariant>(), lf::ValueUsage::Maybe);
|
2023-01-05 14:05:30 +01:00
|
|
|
outputs_.append_as("Attributes", CPPType::get<bke::AnonymousAttributeSet>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
|
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const bool use = params.get_input<bool>(0);
|
|
|
|
|
if (!use) {
|
|
|
|
|
params.set_output<bke::AnonymousAttributeSet>(0, {});
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-12-17 14:00:07 +01:00
|
|
|
const SocketValueVariant *value_variant =
|
|
|
|
|
params.try_get_input_data_ptr_or_request<SocketValueVariant>(1);
|
2023-12-13 13:40:40 +01:00
|
|
|
if (value_variant == nullptr) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/* Wait until the field is computed. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
bke::AnonymousAttributeSet attributes;
|
2023-12-17 14:00:07 +01:00
|
|
|
if (value_variant->is_context_dependent_field()) {
|
|
|
|
|
const GField &field = value_variant->get<GField>();
|
2023-01-05 14:05:30 +01:00
|
|
|
field.node().for_each_field_input_recursive([&](const FieldInput &field_input) {
|
|
|
|
|
if (const auto *attr_field_input = dynamic_cast<const AnonymousAttributeFieldInput *>(
|
|
|
|
|
&field_input))
|
|
|
|
|
{
|
|
|
|
|
if (!attributes.names) {
|
|
|
|
|
attributes.names = std::make_shared<Set<std::string>>();
|
|
|
|
|
}
|
|
|
|
|
attributes.names->add_as(attr_field_input->anonymous_id()->name());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
params.set_output(0, std::move(attributes));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2023-01-06 13:57:21 +11:00
|
|
|
* Conditionally joins multiple attribute sets. Each input attribute set can be disabled with a
|
2023-01-05 14:05:30 +01:00
|
|
|
* corresponding boolean input.
|
|
|
|
|
*/
|
|
|
|
|
class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction {
|
|
|
|
|
const int amount_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForAnonymousAttributeSetJoin(const int amount) : amount_(amount)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Join Attribute Sets";
|
|
|
|
|
for ([[maybe_unused]] const int i : IndexRange(amount)) {
|
|
|
|
|
inputs_.append_as("Use", CPPType::get<bool>());
|
|
|
|
|
inputs_.append_as(
|
|
|
|
|
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe);
|
|
|
|
|
}
|
|
|
|
|
outputs_.append_as("Attribute Set", CPPType::get<bke::AnonymousAttributeSet>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
|
|
|
|
{
|
|
|
|
|
Vector<bke::AnonymousAttributeSet *> sets;
|
|
|
|
|
bool set_is_missing = false;
|
|
|
|
|
for (const int i : IndexRange(amount_)) {
|
|
|
|
|
if (params.get_input<bool>(this->get_use_input(i))) {
|
|
|
|
|
if (bke::AnonymousAttributeSet *set =
|
|
|
|
|
params.try_get_input_data_ptr_or_request<bke::AnonymousAttributeSet>(
|
|
|
|
|
this->get_attribute_set_input(i)))
|
|
|
|
|
{
|
|
|
|
|
sets.append(set);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
set_is_missing = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (set_is_missing) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
bke::AnonymousAttributeSet joined_set;
|
|
|
|
|
if (sets.is_empty()) {
|
|
|
|
|
/* Nothing to do. */
|
|
|
|
|
}
|
|
|
|
|
else if (sets.size() == 1) {
|
|
|
|
|
joined_set.names = std::move(sets[0]->names);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
joined_set.names = std::make_shared<Set<std::string>>();
|
|
|
|
|
for (const bke::AnonymousAttributeSet *set : sets) {
|
|
|
|
|
if (set->names) {
|
|
|
|
|
for (const std::string &name : *set->names) {
|
|
|
|
|
joined_set.names->add(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
params.set_output(0, std::move(joined_set));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_use_input(const int i) const
|
|
|
|
|
{
|
|
|
|
|
return 2 * i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_attribute_set_input(const int i) const
|
|
|
|
|
{
|
|
|
|
|
return 2 * i + 1;
|
|
|
|
|
}
|
2023-01-28 15:28:55 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cache for functions small amounts to avoid to avoid building them many times.
|
|
|
|
|
*/
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
static const LazyFunctionForAnonymousAttributeSetJoin &get_cached(const int amount,
|
|
|
|
|
ResourceScope &scope)
|
2023-01-28 15:28:55 +01:00
|
|
|
{
|
|
|
|
|
constexpr int cache_amount = 16;
|
|
|
|
|
static std::array<LazyFunctionForAnonymousAttributeSetJoin, cache_amount> cached_functions =
|
|
|
|
|
get_cache(std::make_index_sequence<cache_amount>{});
|
2023-01-29 00:13:37 +01:00
|
|
|
if (amount < cached_functions.size()) {
|
2023-01-28 15:28:55 +01:00
|
|
|
return cached_functions[amount];
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
return scope.construct<LazyFunctionForAnonymousAttributeSetJoin>(amount);
|
2023-01-28 15:28:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
template<size_t... I>
|
|
|
|
|
static std::array<LazyFunctionForAnonymousAttributeSetJoin, sizeof...(I)> get_cache(
|
|
|
|
|
std::index_sequence<I...> /*indices*/)
|
|
|
|
|
{
|
|
|
|
|
return {LazyFunctionForAnonymousAttributeSetJoin(I)...};
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
};
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
class LazyFunctionForSimulationZone : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const bNode &sim_output_bnode_;
|
|
|
|
|
const LazyFunction &fn_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForSimulationZone(const bNode &sim_output_bnode, const LazyFunction &fn)
|
|
|
|
|
: sim_output_bnode_(sim_output_bnode), fn_(fn)
|
|
|
|
|
{
|
|
|
|
|
debug_name_ = "Simulation Zone";
|
|
|
|
|
inputs_ = fn.inputs();
|
|
|
|
|
outputs_ = fn.outputs();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
GeoNodesLFUserData &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
|
|
|
|
|
bke::SimulationZoneComputeContext compute_context{user_data.compute_context,
|
|
|
|
|
sim_output_bnode_};
|
|
|
|
|
|
|
|
|
|
GeoNodesLFUserData zone_user_data = user_data;
|
|
|
|
|
zone_user_data.compute_context = &compute_context;
|
2023-11-21 14:27:21 +01:00
|
|
|
zone_user_data.log_socket_values = should_log_socket_values_for_context(
|
|
|
|
|
user_data, compute_context.hash());
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
2023-11-21 14:27:21 +01:00
|
|
|
GeoNodesLFLocalUserData zone_local_user_data{zone_user_data};
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::Context zone_context{context.storage, &zone_user_data, &zone_local_user_data};
|
|
|
|
|
fn_.execute(params, zone_context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *init_storage(LinearAllocator<> &allocator) const override
|
|
|
|
|
{
|
|
|
|
|
return fn_.init_storage(allocator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void destruct_storage(void *storage) const override
|
|
|
|
|
{
|
|
|
|
|
fn_.destruct_storage(storage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string input_name(const int i) const override
|
|
|
|
|
{
|
|
|
|
|
return fn_.input_name(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string output_name(const int i) const override
|
|
|
|
|
{
|
|
|
|
|
return fn_.output_name(i);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using JoinAttributeSetsCache = Map<Vector<lf::OutputSocket *>, lf::OutputSocket *>;
|
|
|
|
|
|
|
|
|
|
struct BuildGraphParams {
|
|
|
|
|
/** Lazy-function graph that nodes and links should be inserted into. */
|
|
|
|
|
lf::Graph &lf_graph;
|
|
|
|
|
/** Map #bNodeSocket to newly generated sockets. Those maps are later used to insert links. */
|
|
|
|
|
MultiValueMap<const bNodeSocket *, lf::InputSocket *> lf_inputs_by_bsocket;
|
|
|
|
|
Map<const bNodeSocket *, lf::OutputSocket *> lf_output_by_bsocket;
|
|
|
|
|
/**
|
|
|
|
|
* Maps sockets to corresponding generated boolean sockets that indicate whether the socket is
|
|
|
|
|
* used or not.
|
|
|
|
|
*/
|
|
|
|
|
Map<const bNodeSocket *, lf::OutputSocket *> usage_by_bsocket;
|
|
|
|
|
/**
|
|
|
|
|
* Nodes that propagate anonymous attributes have to know which of those attributes to propagate.
|
|
|
|
|
* For that they have an attribute set input for each geometry output.
|
|
|
|
|
*/
|
|
|
|
|
Map<const bNodeSocket *, lf::InputSocket *> lf_attribute_set_input_by_output_geometry_bsocket;
|
|
|
|
|
/**
|
|
|
|
|
* Multi-input sockets are split into a separate node that collects all the individual values and
|
|
|
|
|
* then passes them to the main node function as list.
|
|
|
|
|
*/
|
|
|
|
|
Map<const bNodeSocket *, lf::Node *> multi_input_socket_nodes;
|
|
|
|
|
/**
|
|
|
|
|
* This is similar to #lf_inputs_by_bsocket but contains more relevant information when border
|
|
|
|
|
* links are linked to multi-input sockets.
|
|
|
|
|
*/
|
|
|
|
|
Map<const bNodeLink *, lf::InputSocket *> lf_input_by_border_link;
|
|
|
|
|
/**
|
|
|
|
|
* Keeps track of all boolean inputs that indicate whether a socket is used. Links to those
|
|
|
|
|
* sockets may be replaced with a constant-true if necessary to break dependency cycles in
|
|
|
|
|
* #fix_link_cycles.
|
|
|
|
|
*/
|
|
|
|
|
Set<lf::InputSocket *> socket_usage_inputs;
|
|
|
|
|
/**
|
|
|
|
|
* Collect input sockets that anonymous attribute sets based on fields or group inputs have to be
|
|
|
|
|
* linked to later.
|
|
|
|
|
*/
|
|
|
|
|
MultiValueMap<int, lf::InputSocket *> lf_attribute_set_input_by_field_source_index;
|
|
|
|
|
MultiValueMap<int, lf::InputSocket *> lf_attribute_set_input_by_caller_propagation_index;
|
|
|
|
|
/** */
|
|
|
|
|
/** Cache to avoid building the same socket combinations multiple times. */
|
|
|
|
|
Map<Vector<lf::OutputSocket *>, lf::OutputSocket *> socket_usages_combination_cache;
|
|
|
|
|
};
|
|
|
|
|
|
2023-09-17 15:13:30 +02:00
|
|
|
struct ZoneFunctionIndices {
|
|
|
|
|
struct {
|
2024-01-09 18:16:43 +01:00
|
|
|
Vector<int> main;
|
|
|
|
|
Vector<int> border_links;
|
|
|
|
|
Vector<int> output_usages;
|
2023-09-17 15:13:30 +02:00
|
|
|
/**
|
|
|
|
|
* Some attribute sets are input into the body of a zone from the outside. These two
|
|
|
|
|
* maps indicate which zone function inputs corresponds to attribute set. Attribute sets are
|
|
|
|
|
* identified by either a "field source index" or "caller propagation index".
|
|
|
|
|
*/
|
|
|
|
|
Map<int, int> attributes_by_field_source_index;
|
|
|
|
|
Map<int, int> attributes_by_caller_propagation_index;
|
|
|
|
|
} inputs;
|
|
|
|
|
struct {
|
2024-01-09 18:16:43 +01:00
|
|
|
Vector<int> main;
|
|
|
|
|
Vector<int> border_link_usages;
|
|
|
|
|
Vector<int> input_usages;
|
2023-09-17 15:13:30 +02:00
|
|
|
} outputs;
|
|
|
|
|
};
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
struct ZoneBuildInfo {
|
|
|
|
|
/** The lazy function that contains the zone. */
|
|
|
|
|
const LazyFunction *lazy_function = nullptr;
|
|
|
|
|
|
|
|
|
|
/** Information about what the various inputs and outputs of the lazy-function are. */
|
2023-09-17 15:13:30 +02:00
|
|
|
ZoneFunctionIndices indices;
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
};
|
|
|
|
|
|
2023-07-11 22:36:10 +02:00
|
|
|
/**
|
2023-09-16 18:39:54 +02:00
|
|
|
* Contains the lazy-function for the "body" of a zone. It contains all the nodes inside of the
|
|
|
|
|
* zone. The "body" function is wrapped by another lazy-function which represents the zone as a
|
|
|
|
|
* hole. The wrapper function might invoke the zone body multiple times (like for repeat zones).
|
2023-07-11 22:36:10 +02:00
|
|
|
*/
|
2023-09-16 18:39:54 +02:00
|
|
|
struct ZoneBodyFunction {
|
|
|
|
|
const LazyFunction *function = nullptr;
|
2023-09-17 15:13:30 +02:00
|
|
|
ZoneFunctionIndices indices;
|
2023-07-11 22:36:10 +02:00
|
|
|
};
|
|
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
/**
|
|
|
|
|
* Wraps the execution of a repeat loop body. The purpose is to setup the correct #ComputeContext
|
|
|
|
|
* inside of the loop body. This is necessary to support correct logging inside of a repeat zone.
|
|
|
|
|
* An alternative would be to use a separate `LazyFunction` for every iteration, but that would
|
|
|
|
|
* have higher overhead.
|
|
|
|
|
*/
|
|
|
|
|
class RepeatBodyNodeExecuteWrapper : public lf::GraphExecutorNodeExecuteWrapper {
|
|
|
|
|
public:
|
|
|
|
|
const bNode *repeat_output_bnode_ = nullptr;
|
|
|
|
|
VectorSet<lf::FunctionNode *> *lf_body_nodes_ = nullptr;
|
|
|
|
|
|
|
|
|
|
void execute_node(const lf::FunctionNode &node,
|
|
|
|
|
lf::Params ¶ms,
|
|
|
|
|
const lf::Context &context) const
|
|
|
|
|
{
|
|
|
|
|
GeoNodesLFUserData &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
const int iteration = lf_body_nodes_->index_of_try(const_cast<lf::FunctionNode *>(&node));
|
|
|
|
|
const LazyFunction &fn = node.function();
|
|
|
|
|
if (iteration == -1) {
|
|
|
|
|
/* The node is not a loop body node, just execute it normally. */
|
|
|
|
|
fn.execute(params, context);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Setup context for the loop body evaluation. */
|
|
|
|
|
bke::RepeatZoneComputeContext body_compute_context{
|
|
|
|
|
user_data.compute_context, *repeat_output_bnode_, iteration};
|
|
|
|
|
GeoNodesLFUserData body_user_data = user_data;
|
|
|
|
|
body_user_data.compute_context = &body_compute_context;
|
2023-11-21 14:27:21 +01:00
|
|
|
body_user_data.log_socket_values = should_log_socket_values_for_context(
|
|
|
|
|
user_data, body_compute_context.hash());
|
|
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
GeoNodesLFLocalUserData body_local_user_data{body_user_data};
|
|
|
|
|
lf::Context body_context{context.storage, &body_user_data, &body_local_user_data};
|
|
|
|
|
fn.execute(params, body_context);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Knows which iterations of the loop evaluation have side effects.
|
|
|
|
|
*/
|
|
|
|
|
class RepeatZoneSideEffectProvider : public lf::GraphExecutorSideEffectProvider {
|
|
|
|
|
public:
|
|
|
|
|
const bNode *repeat_output_bnode_ = nullptr;
|
|
|
|
|
Span<lf::FunctionNode *> lf_body_nodes_;
|
|
|
|
|
|
|
|
|
|
Vector<const lf::FunctionNode *> get_nodes_with_side_effects(
|
|
|
|
|
const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
GeoNodesLFUserData &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
2023-11-29 13:22:20 +01:00
|
|
|
const GeoNodesCallData &call_data = *user_data.call_data;
|
|
|
|
|
if (!call_data.side_effect_nodes) {
|
2023-09-22 08:58:16 +02:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
const ComputeContextHash &context_hash = user_data.compute_context->hash();
|
|
|
|
|
const Span<int> iterations_with_side_effects =
|
2023-11-29 13:22:20 +01:00
|
|
|
call_data.side_effect_nodes->iterations_by_repeat_zone.lookup(
|
2023-09-22 08:58:16 +02:00
|
|
|
{context_hash, repeat_output_bnode_->identifier});
|
|
|
|
|
|
|
|
|
|
Vector<const lf::FunctionNode *> lf_nodes;
|
|
|
|
|
for (const int i : iterations_with_side_effects) {
|
|
|
|
|
if (i >= 0 && i < lf_body_nodes_.size()) {
|
|
|
|
|
lf_nodes.append(lf_body_nodes_[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return lf_nodes;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct RepeatEvalStorage {
|
|
|
|
|
LinearAllocator<> allocator;
|
|
|
|
|
VectorSet<lf::FunctionNode *> lf_body_nodes;
|
|
|
|
|
lf::Graph graph;
|
|
|
|
|
std::optional<LazyFunctionForLogicalOr> or_function;
|
|
|
|
|
std::optional<RepeatZoneSideEffectProvider> side_effect_provider;
|
|
|
|
|
std::optional<RepeatBodyNodeExecuteWrapper> body_execute_wrapper;
|
|
|
|
|
std::optional<lf::GraphExecutor> graph_executor;
|
|
|
|
|
void *graph_executor_storage = nullptr;
|
|
|
|
|
bool multi_threading_enabled = false;
|
2023-10-06 22:32:51 +02:00
|
|
|
Vector<int> input_index_map;
|
2023-09-22 08:58:16 +02:00
|
|
|
Vector<int> output_index_map;
|
|
|
|
|
};
|
|
|
|
|
|
2023-07-11 22:36:10 +02:00
|
|
|
class LazyFunctionForRepeatZone : public LazyFunction {
|
|
|
|
|
private:
|
|
|
|
|
const bNodeTreeZone &zone_;
|
|
|
|
|
const bNode &repeat_output_bnode_;
|
|
|
|
|
const ZoneBuildInfo &zone_info_;
|
2023-09-16 18:39:54 +02:00
|
|
|
const ZoneBodyFunction &body_fn_;
|
2023-07-11 22:36:10 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LazyFunctionForRepeatZone(const bNodeTreeZone &zone,
|
|
|
|
|
ZoneBuildInfo &zone_info,
|
2023-09-16 18:39:54 +02:00
|
|
|
const ZoneBodyFunction &body_fn)
|
2023-07-11 22:36:10 +02:00
|
|
|
: zone_(zone),
|
|
|
|
|
repeat_output_bnode_(*zone.output_node),
|
|
|
|
|
zone_info_(zone_info),
|
2023-09-16 18:39:54 +02:00
|
|
|
body_fn_(body_fn)
|
2023-07-11 22:36:10 +02:00
|
|
|
{
|
|
|
|
|
debug_name_ = "Repeat Zone";
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.main.append(inputs_.append_and_get_index_as(
|
|
|
|
|
"Iterations", CPPType::get<SocketValueVariant>(), lf::ValueUsage::Used));
|
2023-09-22 08:58:16 +02:00
|
|
|
for (const bNodeSocket *socket : zone.input_node->input_sockets().drop_front(1).drop_back(1)) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.main.append(inputs_.append_and_get_index_as(
|
|
|
|
|
socket->name, *socket->typeinfo->geometry_nodes_cpp_type, lf::ValueUsage::Maybe));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const bNodeLink *link : zone.border_links) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.border_links.append(
|
|
|
|
|
inputs_.append_and_get_index_as(link->fromsock->name,
|
|
|
|
|
*link->tosock->typeinfo->geometry_nodes_cpp_type,
|
|
|
|
|
lf::ValueUsage::Maybe));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const bNodeSocket *socket : zone.output_node->output_sockets().drop_back(1)) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.output_usages.append(
|
|
|
|
|
inputs_.append_and_get_index_as("Usage", CPPType::get<bool>(), lf::ValueUsage::Maybe));
|
|
|
|
|
zone_info.indices.outputs.main.append(outputs_.append_and_get_index_as(
|
|
|
|
|
socket->name, *socket->typeinfo->geometry_nodes_cpp_type));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ([[maybe_unused]] const bNodeSocket *socket :
|
2024-01-02 18:12:54 +01:00
|
|
|
zone.input_node->input_sockets().drop_back(1))
|
|
|
|
|
{
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.outputs.input_usages.append(
|
|
|
|
|
outputs_.append_and_get_index_as("Usage", CPPType::get<bool>()));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
2024-01-09 18:16:43 +01:00
|
|
|
|
2023-07-11 22:36:10 +02:00
|
|
|
for ([[maybe_unused]] const bNodeLink *link : zone.border_links) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.outputs.border_link_usages.append(
|
|
|
|
|
outputs_.append_and_get_index_as("Border Link Usage", CPPType::get<bool>()));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-17 15:13:30 +02:00
|
|
|
for (const auto item : body_fn_.indices.inputs.attributes_by_field_source_index.items()) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.attributes_by_field_source_index.add_new(
|
|
|
|
|
item.key,
|
|
|
|
|
inputs_.append_and_get_index_as(
|
|
|
|
|
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
2023-09-17 15:13:30 +02:00
|
|
|
for (const auto item : body_fn_.indices.inputs.attributes_by_caller_propagation_index.items())
|
|
|
|
|
{
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.attributes_by_caller_propagation_index.add_new(
|
|
|
|
|
item.key,
|
|
|
|
|
inputs_.append_and_get_index_as(
|
|
|
|
|
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
void *init_storage(LinearAllocator<> &allocator) const override
|
2023-07-11 22:36:10 +02:00
|
|
|
{
|
2023-09-22 08:58:16 +02:00
|
|
|
return allocator.construct<RepeatEvalStorage>().release();
|
|
|
|
|
}
|
2023-07-11 22:36:10 +02:00
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
void destruct_storage(void *storage) const override
|
|
|
|
|
{
|
|
|
|
|
RepeatEvalStorage *s = static_cast<RepeatEvalStorage *>(storage);
|
|
|
|
|
if (s->graph_executor_storage) {
|
|
|
|
|
s->graph_executor->destruct_storage(s->graph_executor_storage);
|
|
|
|
|
}
|
|
|
|
|
std::destroy_at(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
|
|
|
|
{
|
2023-09-27 11:09:39 +02:00
|
|
|
auto &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
auto &local_user_data = *static_cast<GeoNodesLFLocalUserData *>(context.local_user_data);
|
|
|
|
|
|
2023-07-11 22:36:10 +02:00
|
|
|
const NodeGeometryRepeatOutput &node_storage = *static_cast<const NodeGeometryRepeatOutput *>(
|
|
|
|
|
repeat_output_bnode_.storage);
|
2023-09-22 08:58:16 +02:00
|
|
|
RepeatEvalStorage &eval_storage = *static_cast<RepeatEvalStorage *>(context.storage);
|
|
|
|
|
|
|
|
|
|
const int iterations_usage_index = zone_info_.indices.outputs.input_usages[0];
|
2023-10-07 23:13:20 +02:00
|
|
|
if (!params.output_was_set(iterations_usage_index)) {
|
2023-09-22 08:58:16 +02:00
|
|
|
/* The iterations input is always used. */
|
|
|
|
|
params.set_output(iterations_usage_index, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!eval_storage.graph_executor) {
|
|
|
|
|
/* Create the execution graph in the first evaluation. */
|
2023-09-27 11:09:39 +02:00
|
|
|
this->initialize_execution_graph(
|
|
|
|
|
params, eval_storage, node_storage, user_data, local_user_data);
|
2023-09-22 08:58:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Execute the graph for the repeat zone. */
|
2023-10-06 22:32:51 +02:00
|
|
|
lf::RemappedParams eval_graph_params{*eval_storage.graph_executor,
|
|
|
|
|
params,
|
|
|
|
|
eval_storage.input_index_map,
|
|
|
|
|
eval_storage.output_index_map,
|
|
|
|
|
eval_storage.multi_threading_enabled};
|
2023-09-22 08:58:16 +02:00
|
|
|
lf::Context eval_graph_context{
|
|
|
|
|
eval_storage.graph_executor_storage, context.user_data, context.local_user_data};
|
|
|
|
|
eval_storage.graph_executor->execute(eval_graph_params, eval_graph_context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate a lazy-function graph that contains the loop body (`body_fn_`) as many times
|
|
|
|
|
* as there are iterations. Since this graph depends on the number of iterations, it can't be
|
|
|
|
|
* reused in general. We could consider caching a version of this graph per number of iterations,
|
|
|
|
|
* but right now that doesn't seem worth it. In practice, it takes much less time to create the
|
|
|
|
|
* graph than to execute it (for intended use cases of this generic implementation, more special
|
|
|
|
|
* case repeat loop evaluations could be implemented separately).
|
|
|
|
|
*/
|
|
|
|
|
void initialize_execution_graph(lf::Params ¶ms,
|
|
|
|
|
RepeatEvalStorage &eval_storage,
|
2023-09-27 11:09:39 +02:00
|
|
|
const NodeGeometryRepeatOutput &node_storage,
|
|
|
|
|
GeoNodesLFUserData &user_data,
|
|
|
|
|
GeoNodesLFLocalUserData &local_user_data) const
|
2023-09-22 08:58:16 +02:00
|
|
|
{
|
|
|
|
|
const int num_repeat_items = node_storage.items_num;
|
|
|
|
|
const int num_border_links = body_fn_.indices.inputs.border_links.size();
|
2023-07-11 22:36:10 +02:00
|
|
|
|
|
|
|
|
/* Number of iterations to evaluate. */
|
|
|
|
|
const int iterations = std::max<int>(
|
2023-12-17 14:00:07 +01:00
|
|
|
0, params.get_input<SocketValueVariant>(zone_info_.indices.inputs.main[0]).get<int>());
|
2023-09-27 11:09:39 +02:00
|
|
|
|
|
|
|
|
/* Show a warning when the inspection index is out of range. */
|
2023-10-09 11:45:25 +02:00
|
|
|
if (node_storage.inspection_index > 0) {
|
|
|
|
|
if (node_storage.inspection_index >= iterations) {
|
|
|
|
|
if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(
|
2024-01-02 18:12:54 +01:00
|
|
|
user_data))
|
|
|
|
|
{
|
2023-10-09 11:45:25 +02:00
|
|
|
tree_logger->node_warnings.append(
|
2024-02-28 22:22:21 +01:00
|
|
|
*tree_logger->allocator,
|
2023-10-09 11:45:25 +02:00
|
|
|
{repeat_output_bnode_.identifier,
|
|
|
|
|
{NodeWarningType::Info, N_("Inspection index is out of range")}});
|
|
|
|
|
}
|
2023-09-27 11:09:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
/* Take iterations input into account. */
|
|
|
|
|
const int main_inputs_offset = 1;
|
2023-07-11 22:36:10 +02:00
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
lf::Graph &lf_graph = eval_storage.graph;
|
2023-07-11 22:36:10 +02:00
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
Vector<lf::GraphInputSocket *> lf_inputs;
|
|
|
|
|
Vector<lf::GraphOutputSocket *> lf_outputs;
|
2023-07-11 22:36:10 +02:00
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
for (const int i : inputs_.index_range()) {
|
|
|
|
|
const lf::Input &input = inputs_[i];
|
|
|
|
|
lf_inputs.append(&lf_graph.add_input(*input.type, input.debug_name));
|
|
|
|
|
}
|
|
|
|
|
for (const int i : outputs_.index_range()) {
|
|
|
|
|
const lf::Output &output = outputs_[i];
|
|
|
|
|
lf_outputs.append(&lf_graph.add_output(*output.type, output.debug_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create body nodes. */
|
|
|
|
|
VectorSet<lf::FunctionNode *> &lf_body_nodes = eval_storage.lf_body_nodes;
|
|
|
|
|
for ([[maybe_unused]] const int i : IndexRange(iterations)) {
|
|
|
|
|
lf::FunctionNode &lf_node = lf_graph.add_function(*body_fn_.function);
|
|
|
|
|
lf_body_nodes.add_new(&lf_node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create nodes for combining border link usages. A border link is used when any of the loop
|
|
|
|
|
* bodies uses the border link, so an "or" node is necessary. */
|
|
|
|
|
Array<lf::FunctionNode *> lf_border_link_usage_or_nodes(num_border_links);
|
|
|
|
|
eval_storage.or_function.emplace(iterations);
|
|
|
|
|
for (const int i : IndexRange(num_border_links)) {
|
|
|
|
|
lf::FunctionNode &lf_node = lf_graph.add_function(*eval_storage.or_function);
|
|
|
|
|
lf_border_link_usage_or_nodes[i] = &lf_node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle body nodes one by one. */
|
|
|
|
|
for (const int iter_i : lf_body_nodes.index_range()) {
|
|
|
|
|
lf::FunctionNode &lf_node = *lf_body_nodes[iter_i];
|
|
|
|
|
for (const int i : IndexRange(num_border_links)) {
|
|
|
|
|
lf_graph.add_link(*lf_inputs[zone_info_.indices.inputs.border_links[i]],
|
|
|
|
|
lf_node.input(body_fn_.indices.inputs.border_links[i]));
|
|
|
|
|
lf_graph.add_link(lf_node.output(body_fn_.indices.outputs.border_link_usages[i]),
|
|
|
|
|
lf_border_link_usage_or_nodes[i]->input(iter_i));
|
|
|
|
|
}
|
2023-09-17 15:13:30 +02:00
|
|
|
for (const auto item : body_fn_.indices.inputs.attributes_by_field_source_index.items()) {
|
2023-09-22 08:58:16 +02:00
|
|
|
lf_graph.add_link(
|
|
|
|
|
*lf_inputs[zone_info_.indices.inputs.attributes_by_field_source_index.lookup(
|
|
|
|
|
item.key)],
|
|
|
|
|
lf_node.input(item.value));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
2023-09-17 15:13:30 +02:00
|
|
|
for (const auto item :
|
2024-01-02 18:12:54 +01:00
|
|
|
body_fn_.indices.inputs.attributes_by_caller_propagation_index.items())
|
|
|
|
|
{
|
2023-09-22 08:58:16 +02:00
|
|
|
lf_graph.add_link(
|
|
|
|
|
*lf_inputs[zone_info_.indices.inputs.attributes_by_caller_propagation_index.lookup(
|
|
|
|
|
item.key)],
|
|
|
|
|
lf_node.input(item.value));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
2023-09-22 08:58:16 +02:00
|
|
|
}
|
2023-07-11 22:36:10 +02:00
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
/* Handle body nodes pair-wise. */
|
|
|
|
|
for (const int iter_i : lf_body_nodes.index_range().drop_back(1)) {
|
|
|
|
|
lf::FunctionNode &lf_node = *lf_body_nodes[iter_i];
|
|
|
|
|
lf::FunctionNode &lf_next_node = *lf_body_nodes[iter_i + 1];
|
|
|
|
|
for (const int i : IndexRange(num_repeat_items)) {
|
|
|
|
|
lf_graph.add_link(lf_node.output(body_fn_.indices.outputs.main[i]),
|
|
|
|
|
lf_next_node.input(body_fn_.indices.inputs.main[i]));
|
|
|
|
|
/* TODO: Add back-link after being able to check for cyclic dependencies. */
|
|
|
|
|
// lf_graph.add_link(lf_next_node.output(body_fn_.indices.outputs.input_usages[i]),
|
|
|
|
|
// lf_node.input(body_fn_.indices.inputs.output_usages[i]));
|
|
|
|
|
static bool static_true = true;
|
|
|
|
|
lf_node.input(body_fn_.indices.inputs.output_usages[i]).set_default_value(&static_true);
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
/* Handle border link usage outputs. */
|
|
|
|
|
for (const int i : IndexRange(num_border_links)) {
|
|
|
|
|
lf_graph.add_link(lf_border_link_usage_or_nodes[i]->output(0),
|
|
|
|
|
*lf_outputs[zone_info_.indices.outputs.border_link_usages[i]]);
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
if (iterations > 0) {
|
|
|
|
|
{
|
|
|
|
|
/* Link first body node to input/output nodes. */
|
|
|
|
|
lf::FunctionNode &lf_first_body_node = *lf_body_nodes[0];
|
|
|
|
|
for (const int i : IndexRange(num_repeat_items)) {
|
|
|
|
|
lf_graph.add_link(*lf_inputs[zone_info_.indices.inputs.main[i + main_inputs_offset]],
|
|
|
|
|
lf_first_body_node.input(body_fn_.indices.inputs.main[i]));
|
|
|
|
|
lf_graph.add_link(
|
|
|
|
|
lf_first_body_node.output(body_fn_.indices.outputs.input_usages[i]),
|
|
|
|
|
*lf_outputs[zone_info_.indices.outputs.input_usages[i + main_inputs_offset]]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
/* Link last body node to input/output nodes. */
|
|
|
|
|
lf::FunctionNode &lf_last_body_node = *lf_body_nodes.as_span().last();
|
|
|
|
|
for (const int i : IndexRange(num_repeat_items)) {
|
|
|
|
|
lf_graph.add_link(lf_last_body_node.output(body_fn_.indices.outputs.main[i]),
|
|
|
|
|
*lf_outputs[zone_info_.indices.outputs.main[i]]);
|
|
|
|
|
lf_graph.add_link(*lf_inputs[zone_info_.indices.inputs.output_usages[i]],
|
|
|
|
|
lf_last_body_node.input(body_fn_.indices.inputs.output_usages[i]));
|
|
|
|
|
}
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-22 08:58:16 +02:00
|
|
|
else {
|
|
|
|
|
/* There are no iterations, just link the input directly to the output. */
|
|
|
|
|
for (const int i : IndexRange(num_repeat_items)) {
|
|
|
|
|
lf_graph.add_link(*lf_inputs[zone_info_.indices.inputs.main[i + main_inputs_offset]],
|
|
|
|
|
*lf_outputs[zone_info_.indices.outputs.main[i]]);
|
|
|
|
|
lf_graph.add_link(
|
|
|
|
|
*lf_inputs[zone_info_.indices.inputs.output_usages[i]],
|
|
|
|
|
*lf_outputs[zone_info_.indices.outputs.input_usages[i + main_inputs_offset]]);
|
|
|
|
|
}
|
|
|
|
|
for (const int i : IndexRange(num_border_links)) {
|
|
|
|
|
static bool static_false = false;
|
|
|
|
|
lf_outputs[zone_info_.indices.outputs.border_link_usages[i]]->set_default_value(
|
|
|
|
|
&static_false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The graph is ready, update the node indices which are required by the executor. */
|
|
|
|
|
lf_graph.update_node_indices();
|
|
|
|
|
|
|
|
|
|
// std::cout << "\n\n" << lf_graph.to_dot() << "\n\n";
|
|
|
|
|
|
|
|
|
|
/* Create a mapping from parameter indices inside of this graph to parameters of the repeat
|
|
|
|
|
* zone. The main complexity below stems from the fact that the iterations input is handled
|
|
|
|
|
* outside of this graph. */
|
|
|
|
|
eval_storage.output_index_map.reinitialize(outputs_.size() - 1);
|
2023-10-06 22:32:51 +02:00
|
|
|
eval_storage.input_index_map.resize(inputs_.size() - 1);
|
2023-10-06 23:00:29 +02:00
|
|
|
array_utils::fill_index_range<int>(eval_storage.input_index_map, 1);
|
2023-09-22 08:58:16 +02:00
|
|
|
|
|
|
|
|
Vector<const lf::GraphInputSocket *> lf_graph_inputs = lf_inputs.as_span().drop_front(1);
|
|
|
|
|
|
|
|
|
|
const int iteration_usage_index = zone_info_.indices.outputs.input_usages[0];
|
2023-10-06 23:00:29 +02:00
|
|
|
array_utils::fill_index_range<int>(
|
|
|
|
|
eval_storage.output_index_map.as_mutable_span().take_front(iteration_usage_index));
|
|
|
|
|
array_utils::fill_index_range<int>(
|
|
|
|
|
eval_storage.output_index_map.as_mutable_span().drop_front(iteration_usage_index),
|
|
|
|
|
iteration_usage_index + 1);
|
|
|
|
|
|
2023-09-22 08:58:16 +02:00
|
|
|
Vector<const lf::GraphOutputSocket *> lf_graph_outputs = lf_outputs.as_span().take_front(
|
|
|
|
|
iteration_usage_index);
|
|
|
|
|
lf_graph_outputs.extend(lf_outputs.as_span().drop_front(iteration_usage_index + 1));
|
|
|
|
|
|
|
|
|
|
eval_storage.body_execute_wrapper.emplace();
|
|
|
|
|
eval_storage.body_execute_wrapper->repeat_output_bnode_ = &repeat_output_bnode_;
|
|
|
|
|
eval_storage.body_execute_wrapper->lf_body_nodes_ = &lf_body_nodes;
|
|
|
|
|
eval_storage.side_effect_provider.emplace();
|
|
|
|
|
eval_storage.side_effect_provider->repeat_output_bnode_ = &repeat_output_bnode_;
|
|
|
|
|
eval_storage.side_effect_provider->lf_body_nodes_ = lf_body_nodes;
|
|
|
|
|
|
|
|
|
|
eval_storage.graph_executor.emplace(lf_graph,
|
|
|
|
|
std::move(lf_graph_inputs),
|
|
|
|
|
std::move(lf_graph_outputs),
|
|
|
|
|
nullptr,
|
|
|
|
|
&*eval_storage.side_effect_provider,
|
|
|
|
|
&*eval_storage.body_execute_wrapper);
|
|
|
|
|
eval_storage.graph_executor_storage = eval_storage.graph_executor->init_storage(
|
|
|
|
|
eval_storage.allocator);
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string input_name(const int i) const override
|
|
|
|
|
{
|
2023-09-17 15:13:30 +02:00
|
|
|
if (zone_info_.indices.inputs.output_usages.contains(i)) {
|
2023-07-11 22:36:10 +02:00
|
|
|
const bNodeSocket &bsocket = zone_.output_node->output_socket(
|
2023-09-17 15:13:30 +02:00
|
|
|
i - zone_info_.indices.inputs.output_usages.first());
|
2023-07-11 22:36:10 +02:00
|
|
|
return "Usage: " + StringRef(bsocket.name);
|
|
|
|
|
}
|
|
|
|
|
return inputs_[i].debug_name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string output_name(const int i) const override
|
|
|
|
|
{
|
2023-09-17 15:13:30 +02:00
|
|
|
if (zone_info_.indices.outputs.input_usages.contains(i)) {
|
2023-07-11 22:36:10 +02:00
|
|
|
const bNodeSocket &bsocket = zone_.input_node->input_socket(
|
2023-09-17 15:13:30 +02:00
|
|
|
i - zone_info_.indices.outputs.input_usages.first());
|
2023-07-11 22:36:10 +02:00
|
|
|
return "Usage: " + StringRef(bsocket.name);
|
|
|
|
|
}
|
|
|
|
|
return outputs_[i].debug_name;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-09-17 19:21:47 +02:00
|
|
|
/**
|
|
|
|
|
* Logs intermediate values from the lazy-function graph evaluation into #GeoModifierLog based on
|
|
|
|
|
* the mapping between the lazy-function graph and the corresponding #bNodeTree.
|
|
|
|
|
*/
|
|
|
|
|
class GeometryNodesLazyFunctionLogger : public lf::GraphExecutor::Logger {
|
|
|
|
|
private:
|
|
|
|
|
const GeometryNodesLazyFunctionGraphInfo &lf_graph_info_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
GeometryNodesLazyFunctionLogger(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
|
|
|
|
|
: lf_graph_info_(lf_graph_info)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void log_socket_value(const lf::Socket &lf_socket,
|
|
|
|
|
const GPointer value,
|
|
|
|
|
const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
auto &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
if (!user_data.log_socket_values) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto &local_user_data = *static_cast<GeoNodesLFLocalUserData *>(context.local_user_data);
|
2023-09-21 14:35:20 +02:00
|
|
|
geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(user_data);
|
2023-09-17 19:21:47 +02:00
|
|
|
if (tree_logger == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Span<const bNodeSocket *> bsockets =
|
|
|
|
|
lf_graph_info_.mapping.bsockets_by_lf_socket_map.lookup(&lf_socket);
|
|
|
|
|
if (bsockets.is_empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const bNodeSocket *bsocket : bsockets) {
|
|
|
|
|
/* Avoid logging to some sockets when the same value will also be logged to a linked socket.
|
|
|
|
|
* This reduces the number of logged values without losing information. */
|
|
|
|
|
if (bsocket->is_input() && bsocket->is_directly_linked()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const bNode &bnode = bsocket->owner_node();
|
|
|
|
|
if (bnode.is_reroute()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
tree_logger->log_value(bsocket->owner_node(), *bsocket, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline std::mutex dump_error_context_mutex;
|
|
|
|
|
|
|
|
|
|
void dump_when_outputs_are_missing(const lf::FunctionNode &node,
|
|
|
|
|
Span<const lf::OutputSocket *> missing_sockets,
|
|
|
|
|
const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock{dump_error_context_mutex};
|
|
|
|
|
|
|
|
|
|
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
BLI_assert(user_data != nullptr);
|
|
|
|
|
user_data->compute_context->print_stack(std::cout, node.name());
|
|
|
|
|
std::cout << "Missing outputs:\n";
|
|
|
|
|
for (const lf::OutputSocket *socket : missing_sockets) {
|
|
|
|
|
std::cout << " " << socket->name() << "\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dump_when_input_is_set_twice(const lf::InputSocket &target_socket,
|
|
|
|
|
const lf::OutputSocket &from_socket,
|
|
|
|
|
const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock{dump_error_context_mutex};
|
|
|
|
|
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << from_socket.node().name() << ":" << from_socket.name() << " -> "
|
|
|
|
|
<< target_socket.node().name() << ":" << target_socket.name();
|
|
|
|
|
|
|
|
|
|
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
BLI_assert(user_data != nullptr);
|
|
|
|
|
user_data->compute_context->print_stack(std::cout, ss.str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void log_before_node_execute(const lf::FunctionNode &node,
|
|
|
|
|
const lf::Params & /*params*/,
|
|
|
|
|
const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
/* Enable this to see the threads that invoked a node. */
|
|
|
|
|
if constexpr (false) {
|
|
|
|
|
this->add_thread_id_debug_message(node, context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void add_thread_id_debug_message(const lf::FunctionNode &node, const lf::Context &context) const
|
|
|
|
|
{
|
|
|
|
|
static std::atomic<int> thread_id_source = 0;
|
|
|
|
|
static thread_local const int thread_id = thread_id_source.fetch_add(1);
|
|
|
|
|
static thread_local const std::string thread_id_str = "Thread: " + std::to_string(thread_id);
|
|
|
|
|
|
2023-09-21 14:35:20 +02:00
|
|
|
const auto &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
|
2023-09-17 19:21:47 +02:00
|
|
|
const auto &local_user_data = *static_cast<GeoNodesLFLocalUserData *>(context.local_user_data);
|
2023-09-21 14:35:20 +02:00
|
|
|
geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(user_data);
|
2023-09-17 19:21:47 +02:00
|
|
|
if (tree_logger == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find corresponding node based on the socket mapping. */
|
|
|
|
|
auto check_sockets = [&](const Span<const lf::Socket *> lf_sockets) {
|
|
|
|
|
for (const lf::Socket *lf_socket : lf_sockets) {
|
|
|
|
|
const Span<const bNodeSocket *> bsockets =
|
|
|
|
|
lf_graph_info_.mapping.bsockets_by_lf_socket_map.lookup(lf_socket);
|
|
|
|
|
if (!bsockets.is_empty()) {
|
|
|
|
|
const bNodeSocket &bsocket = *bsockets[0];
|
|
|
|
|
const bNode &bnode = bsocket.owner_node();
|
2024-02-28 22:22:21 +01:00
|
|
|
tree_logger->debug_messages.append(*tree_logger->allocator,
|
|
|
|
|
{bnode.identifier, thread_id_str});
|
2023-09-17 19:21:47 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (check_sockets(node.inputs().cast<const lf::Socket *>())) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
check_sockets(node.outputs().cast<const lf::Socket *>());
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Tells the lazy-function graph evaluator which nodes have side effects based on the current
|
|
|
|
|
* context. For example, the same viewer node can have side effects in one context, but not in
|
|
|
|
|
* another (depending on e.g. which tree path is currently viewed in the node editor).
|
|
|
|
|
*/
|
|
|
|
|
class GeometryNodesLazyFunctionSideEffectProvider : public lf::GraphExecutor::SideEffectProvider {
|
|
|
|
|
public:
|
|
|
|
|
Vector<const lf::FunctionNode *> get_nodes_with_side_effects(
|
|
|
|
|
const lf::Context &context) const override
|
|
|
|
|
{
|
|
|
|
|
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
|
|
|
|
|
BLI_assert(user_data != nullptr);
|
2023-11-29 13:22:20 +01:00
|
|
|
const GeoNodesCallData &call_data = *user_data->call_data;
|
|
|
|
|
if (!call_data.side_effect_nodes) {
|
2023-09-17 19:21:47 +02:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
const ComputeContextHash &context_hash = user_data->compute_context->hash();
|
2023-11-29 13:22:20 +01:00
|
|
|
return call_data.side_effect_nodes->nodes_by_context.lookup(context_hash);
|
2023-09-17 19:21:47 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
/**
|
2023-09-17 19:09:45 +02:00
|
|
|
* Utility class to build a lazy-function based on a geometry nodes tree.
|
2022-09-13 08:44:26 +02:00
|
|
|
* This is mainly a separate class because it makes it easier to have variables that can be
|
|
|
|
|
* accessed by many functions.
|
|
|
|
|
*/
|
2023-09-17 19:09:45 +02:00
|
|
|
struct GeometryNodesLazyFunctionBuilder {
|
2022-09-13 08:44:26 +02:00
|
|
|
private:
|
|
|
|
|
const bNodeTree &btree_;
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const aai::AnonymousAttributeInferencingResult &attribute_inferencing_;
|
|
|
|
|
ResourceScope &scope_;
|
|
|
|
|
NodeMultiFunctions &node_multi_functions_;
|
2022-09-13 08:44:26 +02:00
|
|
|
GeometryNodesLazyFunctionGraphInfo *lf_graph_info_;
|
|
|
|
|
GeometryNodeLazyFunctionGraphMapping *mapping_;
|
|
|
|
|
const bke::DataTypeConversions *conversions_;
|
|
|
|
|
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
/**
|
|
|
|
|
* A #LazyFunctionForSimulationInputsUsage for each simulation zone.
|
|
|
|
|
*/
|
|
|
|
|
Map<const bNode *, lf::Node *> simulation_inputs_usage_nodes_;
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-06-20 10:25:41 +02:00
|
|
|
const bNodeTreeZones *tree_zones_;
|
2023-07-11 22:36:10 +02:00
|
|
|
MutableSpan<ZoneBuildInfo> zone_build_infos_;
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
2023-09-17 19:09:45 +02:00
|
|
|
/**
|
|
|
|
|
* The inputs sockets in the graph. Multiple group input nodes are combined into one in the
|
|
|
|
|
* lazy-function graph.
|
|
|
|
|
*/
|
|
|
|
|
Vector<const lf::GraphInputSocket *> group_input_sockets_;
|
|
|
|
|
/**
|
|
|
|
|
* Interface output sockets that correspond to the active group output node. If there is no such
|
|
|
|
|
* node, defaulted fallback outputs are created.
|
|
|
|
|
*/
|
|
|
|
|
Vector<const lf::GraphOutputSocket *> standard_group_output_sockets_;
|
|
|
|
|
/**
|
|
|
|
|
* Interface boolean sockets that have to be passed in from the outside and indicate whether a
|
|
|
|
|
* specific output will be used.
|
|
|
|
|
*/
|
|
|
|
|
Vector<const lf::GraphInputSocket *> group_output_used_sockets_;
|
|
|
|
|
/**
|
|
|
|
|
* Interface boolean sockets that can be used as group output that indicate whether a specific
|
|
|
|
|
* input will be used (this may depend on the used outputs as well as other inputs).
|
|
|
|
|
*/
|
|
|
|
|
Vector<const lf::GraphOutputSocket *> group_input_usage_sockets_;
|
|
|
|
|
/**
|
|
|
|
|
* If the node group propagates attributes from an input geometry to the output, it has to know
|
|
|
|
|
* which attributes should be propagated and which can be removed (for optimization purposes).
|
|
|
|
|
*/
|
|
|
|
|
Map<int, const lf::GraphInputSocket *> attribute_set_by_geometry_output_;
|
|
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
friend class UsedSocketVisualizeOptions;
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
public:
|
2023-09-17 19:09:45 +02:00
|
|
|
GeometryNodesLazyFunctionBuilder(const bNodeTree &btree,
|
|
|
|
|
GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
: btree_(btree),
|
|
|
|
|
attribute_inferencing_(*btree.runtime->anonymous_attribute_inferencing),
|
|
|
|
|
scope_(lf_graph_info.scope),
|
|
|
|
|
node_multi_functions_(lf_graph_info.scope.construct<NodeMultiFunctions>(btree)),
|
|
|
|
|
lf_graph_info_(&lf_graph_info)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build()
|
|
|
|
|
{
|
|
|
|
|
btree_.ensure_topology_cache();
|
2023-09-14 14:13:07 +02:00
|
|
|
btree_.ensure_interface_cache();
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
mapping_ = &lf_graph_info_->mapping;
|
|
|
|
|
conversions_ = &bke::get_implicit_type_conversions();
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
tree_zones_ = btree_.zones();
|
|
|
|
|
|
|
|
|
|
this->initialize_mapping_arrays();
|
|
|
|
|
this->build_zone_functions();
|
|
|
|
|
this->build_root_graph();
|
2023-09-17 19:09:45 +02:00
|
|
|
this->build_geometry_nodes_group_function();
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
private:
|
|
|
|
|
void initialize_mapping_arrays()
|
|
|
|
|
{
|
2023-04-22 13:01:00 +02:00
|
|
|
mapping_->lf_input_index_for_output_bsocket_usage.reinitialize(
|
|
|
|
|
btree_.all_output_sockets().size());
|
|
|
|
|
mapping_->lf_input_index_for_output_bsocket_usage.fill(-1);
|
|
|
|
|
mapping_->lf_input_index_for_attribute_propagation_to_output.reinitialize(
|
|
|
|
|
btree_.all_output_sockets().size());
|
|
|
|
|
mapping_->lf_input_index_for_attribute_propagation_to_output.fill(-1);
|
2023-04-22 16:14:38 +02:00
|
|
|
mapping_->lf_index_by_bsocket.reinitialize(btree_.all_sockets().size());
|
|
|
|
|
mapping_->lf_index_by_bsocket.fill(-1);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/**
|
|
|
|
|
* Builds lazy-functions for all zones in the node tree.
|
|
|
|
|
*/
|
|
|
|
|
void build_zone_functions()
|
|
|
|
|
{
|
2024-01-09 22:10:11 +01:00
|
|
|
zone_build_infos_ = scope_.construct<Array<ZoneBuildInfo>>(tree_zones_->zones.size());
|
2023-01-05 14:05:30 +01:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const Array<int> zone_build_order = this->compute_zone_build_order();
|
2023-01-05 14:05:30 +01:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
for (const int zone_i : zone_build_order) {
|
2023-06-20 10:25:41 +02:00
|
|
|
const bNodeTreeZone &zone = *tree_zones_->zones[zone_i];
|
2023-07-11 22:36:10 +02:00
|
|
|
switch (zone.output_node->type) {
|
|
|
|
|
case GEO_NODE_SIMULATION_OUTPUT: {
|
|
|
|
|
this->build_simulation_zone_function(zone);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case GEO_NODE_REPEAT_OUTPUT: {
|
|
|
|
|
this->build_repeat_zone_function(zone);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Array<int> compute_zone_build_order()
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/* Build nested zones first. */
|
|
|
|
|
Array<int> zone_build_order(tree_zones_->zones.size());
|
2023-10-06 23:00:29 +02:00
|
|
|
array_utils::fill_index_range<int>(zone_build_order);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
std::sort(
|
|
|
|
|
zone_build_order.begin(), zone_build_order.end(), [&](const int zone_a, const int zone_b) {
|
|
|
|
|
return tree_zones_->zones[zone_a]->depth > tree_zones_->zones[zone_b]->depth;
|
|
|
|
|
});
|
|
|
|
|
return zone_build_order;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/**
|
|
|
|
|
* Builds a lazy-function for a simulation zone.
|
|
|
|
|
* Internally, the generated lazy-function is just another graph.
|
|
|
|
|
*/
|
2023-06-20 10:25:41 +02:00
|
|
|
void build_simulation_zone_function(const bNodeTreeZone &zone)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const int zone_i = zone.index;
|
|
|
|
|
ZoneBuildInfo &zone_info = zone_build_infos_[zone_i];
|
|
|
|
|
lf::Graph &lf_graph = scope_.construct<lf::Graph>();
|
2023-09-09 09:53:01 +02:00
|
|
|
const auto &sim_output_storage = *static_cast<const NodeGeometrySimulationOutput *>(
|
|
|
|
|
zone.output_node->storage);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
Vector<lf::GraphInputSocket *> lf_zone_inputs;
|
|
|
|
|
Vector<lf::GraphOutputSocket *> lf_zone_outputs;
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (zone.input_node != nullptr) {
|
2023-09-17 13:54:09 +02:00
|
|
|
for (const bNodeSocket *bsocket : zone.input_node->input_sockets().drop_back(1)) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.main.append(lf_zone_inputs.append_and_get_index(
|
|
|
|
|
&lf_graph.add_input(*bsocket->typeinfo->geometry_nodes_cpp_type, bsocket->name)));
|
|
|
|
|
zone_info.indices.outputs.input_usages.append(lf_zone_outputs.append_and_get_index(
|
|
|
|
|
&lf_graph.add_output(CPPType::get<bool>(), "Usage: " + StringRef(bsocket->name))));
|
2023-09-17 13:54:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
this->build_zone_border_links_inputs(
|
|
|
|
|
zone, lf_graph, lf_zone_inputs, zone_info.indices.inputs.border_links);
|
|
|
|
|
this->build_zone_border_link_input_usages(
|
|
|
|
|
zone, lf_graph, lf_zone_outputs, zone_info.indices.outputs.border_link_usages);
|
|
|
|
|
|
2023-09-17 13:54:09 +02:00
|
|
|
for (const bNodeSocket *bsocket : zone.output_node->output_sockets().drop_back(1)) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.outputs.main.append(lf_zone_outputs.append_and_get_index(
|
|
|
|
|
&lf_graph.add_output(*bsocket->typeinfo->geometry_nodes_cpp_type, bsocket->name)));
|
|
|
|
|
zone_info.indices.inputs.output_usages.append(lf_zone_inputs.append_and_get_index(
|
|
|
|
|
&lf_graph.add_input(CPPType::get<bool>(), "Usage: " + StringRef(bsocket->name))));
|
2023-09-17 13:54:09 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::Node &lf_simulation_usage_node = [&]() -> lf::Node & {
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForSimulationInputsUsage>(
|
|
|
|
|
*zone.output_node);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::Node &lf_node = lf_graph.add_function(lazy_function);
|
2022-12-16 12:18:49 +01:00
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
for (const int i : zone_info.indices.outputs.input_usages) {
|
|
|
|
|
lf_graph.add_link(lf_node.output(0), *lf_zone_outputs[i]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
2023-09-17 13:54:09 +02:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
return lf_node;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
BuildGraphParams graph_params{lf_graph};
|
|
|
|
|
|
|
|
|
|
lf::FunctionNode *lf_simulation_input = nullptr;
|
|
|
|
|
if (zone.input_node) {
|
|
|
|
|
lf_simulation_input = this->insert_simulation_input_node(
|
|
|
|
|
btree_, *zone.input_node, graph_params);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::FunctionNode &lf_simulation_output = this->insert_simulation_output_node(*zone.output_node,
|
|
|
|
|
graph_params);
|
|
|
|
|
|
|
|
|
|
for (const bNodeSocket *bsocket : zone.output_node->input_sockets().drop_back(1)) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(bsocket, &lf_simulation_usage_node.output(1));
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-09 09:53:01 +02:00
|
|
|
/* Link simulation input node directly to simulation output node for skip behavior. */
|
|
|
|
|
for (const int i : IndexRange(sim_output_storage.items_num)) {
|
|
|
|
|
lf::InputSocket &lf_to = lf_simulation_output.input(i + 1);
|
|
|
|
|
if (lf_simulation_input) {
|
|
|
|
|
lf::OutputSocket &lf_from = lf_simulation_input->output(i + 1);
|
|
|
|
|
lf_graph.add_link(lf_from, lf_to);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
lf_to.set_default_value(lf_to.type().default_value());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->insert_nodes_and_zones(zone.child_nodes, zone.child_zones, graph_params);
|
|
|
|
|
|
|
|
|
|
if (zone.input_node) {
|
|
|
|
|
this->build_output_socket_usages(*zone.input_node, graph_params);
|
|
|
|
|
}
|
|
|
|
|
for (const auto item : graph_params.lf_output_by_bsocket.items()) {
|
|
|
|
|
this->insert_links_from_socket(*item.key, *item.value, graph_params);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
this->link_border_link_inputs_and_usages(zone,
|
|
|
|
|
lf_zone_inputs,
|
|
|
|
|
zone_info.indices.inputs.border_links,
|
|
|
|
|
lf_zone_outputs,
|
|
|
|
|
zone_info.indices.outputs.border_link_usages,
|
|
|
|
|
graph_params);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
for (const int i : zone_info.indices.inputs.main) {
|
|
|
|
|
lf_graph.add_link(*lf_zone_inputs[i], lf_simulation_input->input(i));
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
2023-09-17 13:54:09 +02:00
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
for (const int i : zone_info.indices.outputs.main.index_range()) {
|
|
|
|
|
lf_graph.add_link(lf_simulation_output.output(i),
|
|
|
|
|
*lf_zone_outputs[zone_info.indices.outputs.main[i]]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->add_default_inputs(graph_params);
|
|
|
|
|
|
|
|
|
|
Map<int, lf::OutputSocket *> lf_attribute_set_by_field_source_index;
|
|
|
|
|
Map<int, lf::OutputSocket *> lf_attribute_set_by_caller_propagation_index;
|
|
|
|
|
this->build_attribute_set_inputs_for_zone(graph_params,
|
|
|
|
|
lf_attribute_set_by_field_source_index,
|
2023-07-11 22:36:10 +02:00
|
|
|
lf_attribute_set_by_caller_propagation_index);
|
|
|
|
|
for (const auto item : lf_attribute_set_by_field_source_index.items()) {
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket = *item.value;
|
2023-09-17 13:54:09 +02:00
|
|
|
if (lf_attribute_set_socket.node().is_interface()) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.attributes_by_field_source_index.add_new(
|
|
|
|
|
item.key, lf_zone_inputs.append_and_get_index(&lf_attribute_set_socket));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const auto item : lf_attribute_set_by_caller_propagation_index.items()) {
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket = *item.value;
|
2023-09-17 13:54:09 +02:00
|
|
|
if (lf_attribute_set_socket.node().is_interface()) {
|
2024-01-09 18:16:43 +01:00
|
|
|
zone_info.indices.inputs.attributes_by_caller_propagation_index.add_new(
|
|
|
|
|
item.key, lf_zone_inputs.append_and_get_index(&lf_attribute_set_socket));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->link_attribute_set_inputs(lf_graph,
|
|
|
|
|
graph_params,
|
|
|
|
|
lf_attribute_set_by_field_source_index,
|
|
|
|
|
lf_attribute_set_by_caller_propagation_index);
|
|
|
|
|
this->fix_link_cycles(lf_graph, graph_params.socket_usage_inputs);
|
|
|
|
|
|
|
|
|
|
lf_graph.update_node_indices();
|
|
|
|
|
|
|
|
|
|
auto &logger = scope_.construct<GeometryNodesLazyFunctionLogger>(*lf_graph_info_);
|
|
|
|
|
auto &side_effect_provider = scope_.construct<GeometryNodesLazyFunctionSideEffectProvider>();
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
const auto &lf_graph_fn = scope_.construct<lf::GraphExecutor>(lf_graph,
|
|
|
|
|
lf_zone_inputs.as_span(),
|
|
|
|
|
lf_zone_outputs.as_span(),
|
|
|
|
|
&logger,
|
|
|
|
|
&side_effect_provider,
|
|
|
|
|
nullptr);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const auto &zone_function = scope_.construct<LazyFunctionForSimulationZone>(*zone.output_node,
|
|
|
|
|
lf_graph_fn);
|
|
|
|
|
zone_info.lazy_function = &zone_function;
|
|
|
|
|
|
|
|
|
|
// std::cout << "\n\n" << lf_graph.to_dot() << "\n\n";
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-11 22:36:10 +02:00
|
|
|
/**
|
2023-09-16 18:39:54 +02:00
|
|
|
* Builds a #LazyFunction for a repeat zone.
|
2023-07-11 22:36:10 +02:00
|
|
|
*/
|
|
|
|
|
void build_repeat_zone_function(const bNodeTreeZone &zone)
|
|
|
|
|
{
|
|
|
|
|
ZoneBuildInfo &zone_info = zone_build_infos_[zone.index];
|
2023-09-16 18:39:54 +02:00
|
|
|
/* Build a function for the loop body. */
|
|
|
|
|
ZoneBodyFunction &body_fn = this->build_zone_body_function(zone);
|
|
|
|
|
/* Wrap the loop body by another function that implements the repeat behavior. */
|
|
|
|
|
auto &zone_fn = scope_.construct<LazyFunctionForRepeatZone>(zone, zone_info, body_fn);
|
|
|
|
|
zone_info.lazy_function = &zone_fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build a lazy-function for the "body" of a zone, i.e. for all the nodes within the zone.
|
|
|
|
|
*/
|
|
|
|
|
ZoneBodyFunction &build_zone_body_function(const bNodeTreeZone &zone)
|
|
|
|
|
{
|
2023-07-11 22:36:10 +02:00
|
|
|
lf::Graph &lf_body_graph = scope_.construct<lf::Graph>();
|
|
|
|
|
|
|
|
|
|
BuildGraphParams graph_params{lf_body_graph};
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
Vector<lf::GraphInputSocket *> lf_body_inputs;
|
|
|
|
|
Vector<lf::GraphOutputSocket *> lf_body_outputs;
|
2023-09-16 18:39:54 +02:00
|
|
|
ZoneBodyFunction &body_fn = scope_.construct<ZoneBodyFunction>();
|
2023-07-11 22:36:10 +02:00
|
|
|
|
2023-09-17 13:54:09 +02:00
|
|
|
for (const bNodeSocket *bsocket : zone.input_node->output_sockets().drop_back(1)) {
|
|
|
|
|
lf::GraphInputSocket &lf_input = lf_body_graph.add_input(
|
2023-10-06 18:29:13 +02:00
|
|
|
*bsocket->typeinfo->geometry_nodes_cpp_type, bsocket->name);
|
2023-09-17 13:54:09 +02:00
|
|
|
lf::GraphOutputSocket &lf_input_usage = lf_body_graph.add_output(
|
2023-10-06 18:29:13 +02:00
|
|
|
CPPType::get<bool>(), "Usage: " + StringRef(bsocket->name));
|
2024-01-09 18:16:43 +01:00
|
|
|
body_fn.indices.inputs.main.append(lf_body_inputs.append_and_get_index(&lf_input));
|
|
|
|
|
body_fn.indices.outputs.input_usages.append(
|
|
|
|
|
lf_body_outputs.append_and_get_index(&lf_input_usage));
|
2023-09-17 13:54:09 +02:00
|
|
|
graph_params.lf_output_by_bsocket.add_new(bsocket, &lf_input);
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
this->build_zone_border_links_inputs(
|
|
|
|
|
zone, lf_body_graph, lf_body_inputs, body_fn.indices.inputs.border_links);
|
|
|
|
|
this->build_zone_border_link_input_usages(
|
|
|
|
|
zone, lf_body_graph, lf_body_outputs, body_fn.indices.outputs.border_link_usages);
|
2023-07-11 22:36:10 +02:00
|
|
|
|
2023-09-17 13:54:09 +02:00
|
|
|
for (const bNodeSocket *bsocket : zone.output_node->input_sockets().drop_back(1)) {
|
|
|
|
|
lf::GraphOutputSocket &lf_output = lf_body_graph.add_output(
|
2023-10-06 18:29:13 +02:00
|
|
|
*bsocket->typeinfo->geometry_nodes_cpp_type, bsocket->name);
|
2023-09-17 13:54:09 +02:00
|
|
|
lf::GraphInputSocket &lf_output_usage = lf_body_graph.add_input(
|
2023-10-06 18:29:13 +02:00
|
|
|
CPPType::get<bool>(), "Usage: " + StringRef(bsocket->name));
|
2023-09-17 13:54:09 +02:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_output);
|
|
|
|
|
graph_params.usage_by_bsocket.add(bsocket, &lf_output_usage);
|
2024-01-09 18:16:43 +01:00
|
|
|
body_fn.indices.outputs.main.append(lf_body_outputs.append_and_get_index(&lf_output));
|
|
|
|
|
body_fn.indices.inputs.output_usages.append(
|
|
|
|
|
lf_body_inputs.append_and_get_index(&lf_output_usage));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->insert_nodes_and_zones(zone.child_nodes, zone.child_zones, graph_params);
|
|
|
|
|
|
|
|
|
|
this->build_output_socket_usages(*zone.input_node, graph_params);
|
|
|
|
|
for (const int i : zone.input_node->output_sockets().drop_back(1).index_range()) {
|
|
|
|
|
const bNodeSocket &bsocket = zone.input_node->output_socket(i);
|
|
|
|
|
lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(&bsocket, nullptr);
|
2024-01-09 18:16:43 +01:00
|
|
|
lf::GraphOutputSocket &lf_usage_output =
|
|
|
|
|
*lf_body_outputs[body_fn.indices.outputs.input_usages[i]];
|
2023-07-11 22:36:10 +02:00
|
|
|
if (lf_usage) {
|
|
|
|
|
lf_body_graph.add_link(*lf_usage, lf_usage_output);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
static const bool static_false = false;
|
|
|
|
|
lf_usage_output.set_default_value(&static_false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto item : graph_params.lf_output_by_bsocket.items()) {
|
|
|
|
|
this->insert_links_from_socket(*item.key, *item.value, graph_params);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
this->link_border_link_inputs_and_usages(zone,
|
|
|
|
|
lf_body_inputs,
|
|
|
|
|
body_fn.indices.inputs.border_links,
|
|
|
|
|
lf_body_outputs,
|
|
|
|
|
body_fn.indices.outputs.border_link_usages,
|
|
|
|
|
graph_params);
|
2023-07-11 22:36:10 +02:00
|
|
|
|
|
|
|
|
this->add_default_inputs(graph_params);
|
|
|
|
|
|
|
|
|
|
Map<int, lf::OutputSocket *> lf_attribute_set_by_field_source_index;
|
|
|
|
|
Map<int, lf::OutputSocket *> lf_attribute_set_by_caller_propagation_index;
|
|
|
|
|
|
|
|
|
|
this->build_attribute_set_inputs_for_zone(graph_params,
|
|
|
|
|
lf_attribute_set_by_field_source_index,
|
|
|
|
|
lf_attribute_set_by_caller_propagation_index);
|
|
|
|
|
for (const auto item : lf_attribute_set_by_field_source_index.items()) {
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket = *item.value;
|
2023-09-17 13:54:09 +02:00
|
|
|
if (lf_attribute_set_socket.node().is_interface()) {
|
2024-01-09 18:16:43 +01:00
|
|
|
body_fn.indices.inputs.attributes_by_field_source_index.add_new(
|
|
|
|
|
item.key, lf_body_inputs.append_and_get_index(&lf_attribute_set_socket));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const auto item : lf_attribute_set_by_caller_propagation_index.items()) {
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket = *item.value;
|
2023-09-17 13:54:09 +02:00
|
|
|
if (lf_attribute_set_socket.node().is_interface()) {
|
2024-01-09 18:16:43 +01:00
|
|
|
body_fn.indices.inputs.attributes_by_caller_propagation_index.add_new(
|
|
|
|
|
item.key, lf_body_inputs.append_and_get_index(&lf_attribute_set_socket));
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this->link_attribute_set_inputs(lf_body_graph,
|
|
|
|
|
graph_params,
|
|
|
|
|
lf_attribute_set_by_field_source_index,
|
|
|
|
|
lf_attribute_set_by_caller_propagation_index);
|
|
|
|
|
this->fix_link_cycles(lf_body_graph, graph_params.socket_usage_inputs);
|
|
|
|
|
|
|
|
|
|
lf_body_graph.update_node_indices();
|
|
|
|
|
|
|
|
|
|
auto &logger = scope_.construct<GeometryNodesLazyFunctionLogger>(*lf_graph_info_);
|
|
|
|
|
auto &side_effect_provider = scope_.construct<GeometryNodesLazyFunctionSideEffectProvider>();
|
2024-01-09 18:16:43 +01:00
|
|
|
|
|
|
|
|
body_fn.function = &scope_.construct<lf::GraphExecutor>(lf_body_graph,
|
|
|
|
|
lf_body_inputs.as_span(),
|
|
|
|
|
lf_body_outputs.as_span(),
|
|
|
|
|
&logger,
|
|
|
|
|
&side_effect_provider,
|
|
|
|
|
nullptr);
|
2023-07-11 22:36:10 +02:00
|
|
|
|
|
|
|
|
// std::cout << "\n\n" << lf_body_graph.to_dot() << "\n\n";
|
|
|
|
|
|
2023-09-16 18:39:54 +02:00
|
|
|
return body_fn;
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
void build_zone_border_links_inputs(const bNodeTreeZone &zone,
|
|
|
|
|
lf::Graph &lf_graph,
|
|
|
|
|
Vector<lf::GraphInputSocket *> &r_lf_graph_inputs,
|
|
|
|
|
Vector<int> &r_indices)
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
{
|
|
|
|
|
for (const bNodeLink *border_link : zone.border_links) {
|
2024-01-09 18:16:43 +01:00
|
|
|
r_indices.append(r_lf_graph_inputs.append_and_get_index(
|
2023-09-17 13:54:09 +02:00
|
|
|
&lf_graph.add_input(*border_link->tosock->typeinfo->geometry_nodes_cpp_type,
|
2024-01-09 18:16:43 +01:00
|
|
|
StringRef("Link from ") + border_link->fromsock->name)));
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
void build_zone_border_link_input_usages(const bNodeTreeZone &zone,
|
|
|
|
|
lf::Graph &lf_graph,
|
|
|
|
|
Vector<lf::GraphOutputSocket *> &r_lf_graph_outputs,
|
|
|
|
|
Vector<int> &r_indices)
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
{
|
2023-09-17 13:54:09 +02:00
|
|
|
for (const bNodeLink *border_link : zone.border_links) {
|
2024-01-09 18:16:43 +01:00
|
|
|
r_indices.append(r_lf_graph_outputs.append_and_get_index(&lf_graph.add_output(
|
|
|
|
|
CPPType::get<bool>(), StringRef("Usage: Link from ") + border_link->fromsock->name)));
|
2023-09-17 13:54:09 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_attribute_set_inputs_for_zone(
|
|
|
|
|
BuildGraphParams &graph_params,
|
|
|
|
|
Map<int, lf::OutputSocket *> &lf_attribute_set_by_field_source_index,
|
2023-07-11 22:36:10 +02:00
|
|
|
Map<int, lf::OutputSocket *> &lf_attribute_set_by_caller_propagation_index)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const Vector<int> all_required_field_sources = this->find_all_required_field_source_indices(
|
|
|
|
|
graph_params.lf_attribute_set_input_by_output_geometry_bsocket,
|
|
|
|
|
graph_params.lf_attribute_set_input_by_field_source_index);
|
|
|
|
|
const Vector<int> all_required_caller_propagation_indices =
|
|
|
|
|
this->find_all_required_caller_propagation_indices(
|
|
|
|
|
graph_params.lf_attribute_set_input_by_output_geometry_bsocket,
|
|
|
|
|
graph_params.lf_attribute_set_input_by_caller_propagation_index);
|
|
|
|
|
|
|
|
|
|
Map<int, int> input_by_field_source_index;
|
|
|
|
|
|
|
|
|
|
for (const int field_source_index : all_required_field_sources) {
|
|
|
|
|
const aai::FieldSource &field_source =
|
|
|
|
|
attribute_inferencing_.all_field_sources[field_source_index];
|
|
|
|
|
if ([[maybe_unused]] const auto *input_field_source = std::get_if<aai::InputFieldSource>(
|
|
|
|
|
&field_source.data))
|
|
|
|
|
{
|
|
|
|
|
input_by_field_source_index.add_new(field_source_index,
|
|
|
|
|
input_by_field_source_index.size());
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
else {
|
|
|
|
|
const auto &socket_field_source = std::get<aai::SocketFieldSource>(field_source.data);
|
|
|
|
|
const bNodeSocket &bsocket = *socket_field_source.socket;
|
|
|
|
|
if (lf::OutputSocket *lf_field_socket = graph_params.lf_output_by_bsocket.lookup_default(
|
|
|
|
|
&bsocket, nullptr))
|
|
|
|
|
{
|
|
|
|
|
lf::OutputSocket *lf_usage_socket = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
&bsocket, nullptr);
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket = this->get_extracted_attributes(
|
|
|
|
|
*lf_field_socket,
|
|
|
|
|
lf_usage_socket,
|
|
|
|
|
graph_params.lf_graph,
|
|
|
|
|
graph_params.socket_usage_inputs);
|
|
|
|
|
lf_attribute_set_by_field_source_index.add(field_source_index, &lf_attribute_set_socket);
|
2023-03-14 14:09:29 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
else {
|
|
|
|
|
input_by_field_source_index.add_new(field_source_index,
|
|
|
|
|
input_by_field_source_index.size());
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
|
|
|
|
{
|
2023-09-17 13:54:09 +02:00
|
|
|
Vector<lf::GraphInputSocket *> attribute_set_inputs;
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const int num = input_by_field_source_index.size() +
|
|
|
|
|
all_required_caller_propagation_indices.size();
|
2023-09-17 13:54:09 +02:00
|
|
|
for ([[maybe_unused]] const int i : IndexRange(num)) {
|
|
|
|
|
attribute_set_inputs.append(&graph_params.lf_graph.add_input(
|
|
|
|
|
CPPType::get<bke::AnonymousAttributeSet>(), "Attribute Set"));
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
|
|
|
|
for (const auto item : input_by_field_source_index.items()) {
|
|
|
|
|
const int field_source_index = item.key;
|
|
|
|
|
const int attribute_set_index = item.value;
|
2023-09-17 13:54:09 +02:00
|
|
|
lf::GraphInputSocket &lf_attribute_set_socket = *attribute_set_inputs[attribute_set_index];
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf_attribute_set_by_field_source_index.add(field_source_index, &lf_attribute_set_socket);
|
|
|
|
|
}
|
|
|
|
|
for (const int i : all_required_caller_propagation_indices.index_range()) {
|
|
|
|
|
const int caller_propagation_index = all_required_caller_propagation_indices[i];
|
2023-09-17 13:54:09 +02:00
|
|
|
lf::GraphInputSocket &lf_attribute_set_socket =
|
|
|
|
|
*attribute_set_inputs[input_by_field_source_index.size() + i];
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf_attribute_set_by_caller_propagation_index.add_new(caller_propagation_index,
|
|
|
|
|
&lf_attribute_set_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/**
|
|
|
|
|
* Build the graph that contains all nodes that are not contained in any zone. This graph is
|
|
|
|
|
* called when this geometry nodes node group is evaluated.
|
|
|
|
|
*/
|
|
|
|
|
void build_root_graph()
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::Graph &lf_graph = lf_graph_info_->graph;
|
|
|
|
|
|
|
|
|
|
this->build_group_input_node(lf_graph);
|
|
|
|
|
if (btree_.group_output_node() == nullptr) {
|
|
|
|
|
this->build_fallback_output_node(lf_graph);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
2023-09-17 13:54:09 +02:00
|
|
|
for (const bNodeTreeInterfaceSocket *interface_input : btree_.interface_inputs()) {
|
|
|
|
|
lf::GraphOutputSocket &lf_socket = lf_graph.add_output(
|
2023-10-19 11:48:08 +02:00
|
|
|
CPPType::get<bool>(),
|
|
|
|
|
StringRef("Usage: ") + (interface_input->name ? interface_input->name : ""));
|
2023-09-17 19:09:45 +02:00
|
|
|
group_input_usage_sockets_.append(&lf_socket);
|
2023-09-17 13:54:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<lf::GraphInputSocket *> lf_output_usages;
|
|
|
|
|
for (const bNodeTreeInterfaceSocket *interface_output : btree_.interface_outputs()) {
|
|
|
|
|
lf::GraphInputSocket &lf_socket = lf_graph.add_input(
|
2023-10-19 11:48:08 +02:00
|
|
|
CPPType::get<bool>(),
|
|
|
|
|
StringRef("Usage: ") + (interface_output->name ? interface_output->name : ""));
|
2023-09-17 19:09:45 +02:00
|
|
|
group_output_used_sockets_.append(&lf_socket);
|
2023-09-17 13:54:09 +02:00
|
|
|
lf_output_usages.append(&lf_socket);
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
|
|
|
|
BuildGraphParams graph_params{lf_graph};
|
|
|
|
|
if (const bNode *group_output_bnode = btree_.group_output_node()) {
|
|
|
|
|
for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) {
|
2023-09-17 13:54:09 +02:00
|
|
|
graph_params.usage_by_bsocket.add(bsocket, lf_output_usages[bsocket->index()]);
|
2023-04-22 16:14:38 +02:00
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->insert_nodes_and_zones(
|
|
|
|
|
tree_zones_->nodes_outside_zones, tree_zones_->root_zones, graph_params);
|
|
|
|
|
|
|
|
|
|
for (const auto item : graph_params.lf_output_by_bsocket.items()) {
|
|
|
|
|
this->insert_links_from_socket(*item.key, *item.value, graph_params);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->build_group_input_usages(graph_params);
|
|
|
|
|
this->add_default_inputs(graph_params);
|
2022-09-13 08:44:26 +02:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->build_attribute_propagation_input_node(lf_graph);
|
2022-09-13 08:44:26 +02:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Map<int, lf::OutputSocket *> lf_attribute_set_by_field_source_index;
|
|
|
|
|
Map<int, lf::OutputSocket *> lf_attribute_set_by_caller_propagation_index;
|
|
|
|
|
this->build_attribute_set_inputs_outside_of_zones(
|
|
|
|
|
graph_params,
|
|
|
|
|
lf_attribute_set_by_field_source_index,
|
|
|
|
|
lf_attribute_set_by_caller_propagation_index);
|
|
|
|
|
this->link_attribute_set_inputs(lf_graph,
|
|
|
|
|
graph_params,
|
|
|
|
|
lf_attribute_set_by_field_source_index,
|
|
|
|
|
lf_attribute_set_by_caller_propagation_index);
|
|
|
|
|
|
|
|
|
|
this->fix_link_cycles(lf_graph, graph_params.socket_usage_inputs);
|
|
|
|
|
|
|
|
|
|
// std::cout << "\n\n" << lf_graph.to_dot() << "\n\n";
|
|
|
|
|
|
|
|
|
|
lf_graph.update_node_indices();
|
|
|
|
|
lf_graph_info_->num_inline_nodes_approximate += lf_graph.nodes().size();
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-17 19:09:45 +02:00
|
|
|
/**
|
|
|
|
|
* Build a lazy-function from the generated graph. This is then the lazy-function that must be
|
|
|
|
|
* executed by others to run a geometry node group.
|
|
|
|
|
*/
|
|
|
|
|
void build_geometry_nodes_group_function()
|
|
|
|
|
{
|
|
|
|
|
GeometryNodesGroupFunction &function = lf_graph_info_->function;
|
|
|
|
|
|
|
|
|
|
Vector<const lf::GraphInputSocket *> lf_graph_inputs;
|
|
|
|
|
Vector<const lf::GraphOutputSocket *> lf_graph_outputs;
|
|
|
|
|
|
|
|
|
|
lf_graph_inputs.extend(group_input_sockets_);
|
|
|
|
|
function.inputs.main = lf_graph_inputs.index_range().take_back(group_input_sockets_.size());
|
|
|
|
|
|
|
|
|
|
lf_graph_inputs.extend(group_output_used_sockets_);
|
|
|
|
|
function.inputs.output_usages = lf_graph_inputs.index_range().take_back(
|
|
|
|
|
group_output_used_sockets_.size());
|
|
|
|
|
|
|
|
|
|
for (auto [output_index, lf_socket] : attribute_set_by_geometry_output_.items()) {
|
|
|
|
|
lf_graph_inputs.append(lf_socket);
|
|
|
|
|
function.inputs.attributes_to_propagate.geometry_outputs.append(output_index);
|
|
|
|
|
}
|
|
|
|
|
function.inputs.attributes_to_propagate.range = lf_graph_inputs.index_range().take_back(
|
|
|
|
|
attribute_set_by_geometry_output_.size());
|
|
|
|
|
|
|
|
|
|
lf_graph_outputs.extend(standard_group_output_sockets_);
|
|
|
|
|
function.outputs.main = lf_graph_outputs.index_range().take_back(
|
|
|
|
|
standard_group_output_sockets_.size());
|
|
|
|
|
|
|
|
|
|
lf_graph_outputs.extend(group_input_usage_sockets_);
|
|
|
|
|
function.outputs.input_usages = lf_graph_outputs.index_range().take_back(
|
|
|
|
|
group_input_usage_sockets_.size());
|
|
|
|
|
|
|
|
|
|
function.function = &scope_.construct<lf::GraphExecutor>(
|
|
|
|
|
lf_graph_info_->graph,
|
|
|
|
|
std::move(lf_graph_inputs),
|
|
|
|
|
std::move(lf_graph_outputs),
|
|
|
|
|
&scope_.construct<GeometryNodesLazyFunctionLogger>(*lf_graph_info_),
|
|
|
|
|
&scope_.construct<GeometryNodesLazyFunctionSideEffectProvider>(),
|
|
|
|
|
nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_attribute_set_inputs_outside_of_zones(
|
|
|
|
|
BuildGraphParams &graph_params,
|
|
|
|
|
Map<int, lf::OutputSocket *> &lf_attribute_set_by_field_source_index,
|
|
|
|
|
Map<int, lf::OutputSocket *> &lf_attribute_set_by_caller_propagation_index)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const Vector<int> all_required_field_sources = this->find_all_required_field_source_indices(
|
|
|
|
|
graph_params.lf_attribute_set_input_by_output_geometry_bsocket,
|
|
|
|
|
graph_params.lf_attribute_set_input_by_field_source_index);
|
|
|
|
|
|
|
|
|
|
for (const int field_source_index : all_required_field_sources) {
|
|
|
|
|
const aai::FieldSource &field_source =
|
|
|
|
|
attribute_inferencing_.all_field_sources[field_source_index];
|
|
|
|
|
lf::OutputSocket *lf_attribute_set_socket;
|
|
|
|
|
if (const auto *input_field_source = std::get_if<aai::InputFieldSource>(&field_source.data))
|
|
|
|
|
{
|
|
|
|
|
const int input_index = input_field_source->input_index;
|
|
|
|
|
lf::OutputSocket &lf_field_socket = const_cast<lf::OutputSocket &>(
|
2023-09-17 19:09:45 +02:00
|
|
|
*group_input_sockets_[input_index]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::OutputSocket *lf_usage_socket = const_cast<lf::OutputSocket *>(
|
2023-09-17 19:09:45 +02:00
|
|
|
group_input_usage_sockets_[input_index]->origin());
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf_attribute_set_socket = &this->get_extracted_attributes(
|
|
|
|
|
lf_field_socket,
|
|
|
|
|
lf_usage_socket,
|
|
|
|
|
graph_params.lf_graph,
|
|
|
|
|
graph_params.socket_usage_inputs);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const auto &socket_field_source = std::get<aai::SocketFieldSource>(field_source.data);
|
|
|
|
|
const bNodeSocket &bsocket = *socket_field_source.socket;
|
|
|
|
|
lf::OutputSocket &lf_field_socket = *graph_params.lf_output_by_bsocket.lookup(&bsocket);
|
|
|
|
|
lf::OutputSocket *lf_usage_socket = graph_params.usage_by_bsocket.lookup_default(&bsocket,
|
|
|
|
|
nullptr);
|
|
|
|
|
lf_attribute_set_socket = &this->get_extracted_attributes(
|
|
|
|
|
lf_field_socket,
|
|
|
|
|
lf_usage_socket,
|
|
|
|
|
graph_params.lf_graph,
|
|
|
|
|
graph_params.socket_usage_inputs);
|
|
|
|
|
}
|
|
|
|
|
lf_attribute_set_by_field_source_index.add_new(field_source_index, lf_attribute_set_socket);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const int caller_propagation_index :
|
2023-06-21 22:36:58 +02:00
|
|
|
attribute_inferencing_.propagated_output_geometry_indices.index_range())
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
{
|
|
|
|
|
const int group_output_index =
|
|
|
|
|
attribute_inferencing_.propagated_output_geometry_indices[caller_propagation_index];
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket = const_cast<lf::OutputSocket &>(
|
2023-09-17 19:09:45 +02:00
|
|
|
*attribute_set_by_geometry_output_.lookup(group_output_index));
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf_attribute_set_by_caller_propagation_index.add(caller_propagation_index,
|
|
|
|
|
&lf_attribute_set_socket);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Vector<int> find_all_required_field_source_indices(
|
|
|
|
|
const Map<const bNodeSocket *, lf::InputSocket *>
|
|
|
|
|
&lf_attribute_set_input_by_output_geometry_bsocket,
|
|
|
|
|
const MultiValueMap<int, lf::InputSocket *> &lf_attribute_set_input_by_field_source_index)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
BitVector<> all_required_field_sources(attribute_inferencing_.all_field_sources.size(), false);
|
|
|
|
|
for (const bNodeSocket *geometry_output_bsocket :
|
|
|
|
|
lf_attribute_set_input_by_output_geometry_bsocket.keys())
|
|
|
|
|
{
|
|
|
|
|
all_required_field_sources |=
|
|
|
|
|
attribute_inferencing_
|
|
|
|
|
.required_fields_by_geometry_socket[geometry_output_bsocket->index_in_tree()];
|
|
|
|
|
}
|
|
|
|
|
for (const int field_source_index : lf_attribute_set_input_by_field_source_index.keys()) {
|
|
|
|
|
all_required_field_sources[field_source_index].set();
|
2022-12-16 18:30:47 +01:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Vector<int> indices;
|
|
|
|
|
bits::foreach_1_index(all_required_field_sources, [&](const int i) { indices.append(i); });
|
|
|
|
|
return indices;
|
|
|
|
|
}
|
2022-12-16 12:18:49 +01:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Vector<int> find_all_required_caller_propagation_indices(
|
|
|
|
|
const Map<const bNodeSocket *, lf::InputSocket *>
|
|
|
|
|
&lf_attribute_set_input_by_output_geometry_bsocket,
|
|
|
|
|
const MultiValueMap<int, lf::InputSocket *>
|
|
|
|
|
&lf_attribute_set_input_by_caller_propagation_index)
|
|
|
|
|
{
|
|
|
|
|
BitVector<> all_required_caller_propagation_indices(
|
|
|
|
|
attribute_inferencing_.propagated_output_geometry_indices.size(), false);
|
|
|
|
|
for (const bNodeSocket *geometry_output_bs :
|
|
|
|
|
lf_attribute_set_input_by_output_geometry_bsocket.keys())
|
|
|
|
|
{
|
|
|
|
|
all_required_caller_propagation_indices |=
|
|
|
|
|
attribute_inferencing_
|
|
|
|
|
.propagate_to_output_by_geometry_socket[geometry_output_bs->index_in_tree()];
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
for (const int caller_propagation_index :
|
|
|
|
|
lf_attribute_set_input_by_caller_propagation_index.keys())
|
|
|
|
|
{
|
|
|
|
|
all_required_caller_propagation_indices[caller_propagation_index].set();
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2022-12-16 12:18:49 +01:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Vector<int> indices;
|
|
|
|
|
bits::foreach_1_index(all_required_caller_propagation_indices,
|
|
|
|
|
[&](const int i) { indices.append(i); });
|
|
|
|
|
return indices;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void link_attribute_set_inputs(
|
|
|
|
|
lf::Graph &lf_graph,
|
|
|
|
|
BuildGraphParams &graph_params,
|
|
|
|
|
const Map<int, lf::OutputSocket *> &lf_attribute_set_by_field_source_index,
|
|
|
|
|
const Map<int, lf::OutputSocket *> &lf_attribute_set_by_caller_propagation_index)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
JoinAttributeSetsCache join_attribute_sets_cache;
|
2022-09-13 08:44:26 +02:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
for (const MapItem<const bNodeSocket *, lf::InputSocket *> item :
|
|
|
|
|
graph_params.lf_attribute_set_input_by_output_geometry_bsocket.items())
|
|
|
|
|
{
|
|
|
|
|
const bNodeSocket &geometry_output_bsocket = *item.key;
|
|
|
|
|
lf::InputSocket &lf_attribute_set_input = *item.value;
|
|
|
|
|
|
|
|
|
|
Vector<lf::OutputSocket *> lf_attribute_set_sockets;
|
|
|
|
|
|
|
|
|
|
const BoundedBitSpan required_fields =
|
|
|
|
|
attribute_inferencing_
|
|
|
|
|
.required_fields_by_geometry_socket[geometry_output_bsocket.index_in_tree()];
|
|
|
|
|
bits::foreach_1_index(required_fields, [&](const int field_source_index) {
|
|
|
|
|
const auto &field_source = attribute_inferencing_.all_field_sources[field_source_index];
|
|
|
|
|
if (const auto *socket_field_source = std::get_if<aai::SocketFieldSource>(
|
2024-01-02 18:12:54 +01:00
|
|
|
&field_source.data))
|
|
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (&socket_field_source->socket->owner_node() == &geometry_output_bsocket.owner_node())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lf_attribute_set_sockets.append(
|
|
|
|
|
lf_attribute_set_by_field_source_index.lookup(field_source_index));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const BoundedBitSpan required_caller_propagations =
|
|
|
|
|
attribute_inferencing_
|
|
|
|
|
.propagate_to_output_by_geometry_socket[geometry_output_bsocket.index_in_tree()];
|
|
|
|
|
bits::foreach_1_index(required_caller_propagations, [&](const int caller_propagation_index) {
|
|
|
|
|
lf_attribute_set_sockets.append(
|
|
|
|
|
lf_attribute_set_by_caller_propagation_index.lookup(caller_propagation_index));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (lf::OutputSocket *lf_attribute_set = this->join_attribute_sets(
|
|
|
|
|
lf_attribute_set_sockets,
|
|
|
|
|
join_attribute_sets_cache,
|
|
|
|
|
lf_graph,
|
|
|
|
|
graph_params.socket_usage_inputs))
|
|
|
|
|
{
|
|
|
|
|
lf_graph.add_link(*lf_attribute_set, lf_attribute_set_input);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
static const bke::AnonymousAttributeSet empty_set;
|
|
|
|
|
lf_attribute_set_input.set_default_value(&empty_set);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto item : graph_params.lf_attribute_set_input_by_field_source_index.items()) {
|
|
|
|
|
const int field_source_index = item.key;
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket = *lf_attribute_set_by_field_source_index.lookup(
|
|
|
|
|
field_source_index);
|
|
|
|
|
for (lf::InputSocket *lf_attribute_set_input : item.value) {
|
|
|
|
|
lf_graph.add_link(lf_attribute_set_socket, *lf_attribute_set_input);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const auto item : graph_params.lf_attribute_set_input_by_caller_propagation_index.items())
|
|
|
|
|
{
|
|
|
|
|
const int caller_propagation_index = item.key;
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket =
|
|
|
|
|
*lf_attribute_set_by_caller_propagation_index.lookup(caller_propagation_index);
|
|
|
|
|
for (lf::InputSocket *lf_attribute_set_input : item.value) {
|
|
|
|
|
lf_graph.add_link(lf_attribute_set_socket, *lf_attribute_set_input);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void insert_nodes_and_zones(const Span<const bNode *> bnodes,
|
2023-06-20 10:25:41 +02:00
|
|
|
const Span<const bNodeTreeZone *> zones,
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
Vector<const bNode *> nodes_to_insert = bnodes;
|
2023-06-20 10:25:41 +02:00
|
|
|
Map<const bNode *, const bNodeTreeZone *> zone_by_output;
|
|
|
|
|
for (const bNodeTreeZone *zone : zones) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
nodes_to_insert.append(zone->output_node);
|
|
|
|
|
zone_by_output.add(zone->output_node, zone);
|
|
|
|
|
}
|
|
|
|
|
/* Insert nodes from right to left so that usage sockets can be build in the same pass. */
|
|
|
|
|
std::sort(nodes_to_insert.begin(), nodes_to_insert.end(), [](const bNode *a, const bNode *b) {
|
|
|
|
|
return a->runtime->toposort_right_to_left_index < b->runtime->toposort_right_to_left_index;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const bNode *bnode : nodes_to_insert) {
|
|
|
|
|
this->build_output_socket_usages(*bnode, graph_params);
|
2023-06-20 10:25:41 +02:00
|
|
|
if (const bNodeTreeZone *zone = zone_by_output.lookup_default(bnode, nullptr)) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->insert_child_zone_node(*zone, graph_params);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
this->insert_node_in_graph(*bnode, graph_params);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 18:16:43 +01:00
|
|
|
void link_border_link_inputs_and_usages(const bNodeTreeZone &zone,
|
|
|
|
|
const Span<lf::GraphInputSocket *> lf_inputs,
|
|
|
|
|
const Span<int> lf_border_link_input_indices,
|
|
|
|
|
const Span<lf::GraphOutputSocket *> lf_usages,
|
|
|
|
|
const Span<int> lf_border_link_usage_indices,
|
|
|
|
|
BuildGraphParams &graph_params)
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
{
|
|
|
|
|
lf::Graph &lf_graph = graph_params.lf_graph;
|
|
|
|
|
for (const int border_link_i : zone.border_links.index_range()) {
|
|
|
|
|
const bNodeLink &border_link = *zone.border_links[border_link_i];
|
2024-01-09 18:16:43 +01:00
|
|
|
lf::GraphInputSocket &lf_from = *lf_inputs[lf_border_link_input_indices[border_link_i]];
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const Vector<lf::InputSocket *> lf_link_targets = this->find_link_targets(border_link,
|
|
|
|
|
graph_params);
|
|
|
|
|
for (lf::InputSocket *lf_to : lf_link_targets) {
|
|
|
|
|
lf_graph.add_link(lf_from, *lf_to);
|
|
|
|
|
}
|
2024-01-09 18:16:43 +01:00
|
|
|
lf::GraphOutputSocket &lf_usage_output =
|
|
|
|
|
*lf_usages[lf_border_link_usage_indices[border_link_i]];
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
border_link.tosock, nullptr))
|
|
|
|
|
{
|
|
|
|
|
lf_graph.add_link(*lf_usage, lf_usage_output);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
static const bool static_false = false;
|
|
|
|
|
lf_usage_output.set_default_value(&static_false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lf::OutputSocket &get_extracted_attributes(lf::OutputSocket &lf_field_socket,
|
|
|
|
|
lf::OutputSocket *lf_usage_socket,
|
|
|
|
|
lf::Graph &lf_graph,
|
|
|
|
|
Set<lf::InputSocket *> &socket_usage_inputs)
|
|
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForAnonymousAttributeSetExtract>();
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::Node &lf_node = lf_graph.add_function(lazy_function);
|
|
|
|
|
lf::InputSocket &lf_use_input = lf_node.input(0);
|
|
|
|
|
lf::InputSocket &lf_field_input = lf_node.input(1);
|
|
|
|
|
socket_usage_inputs.add_new(&lf_use_input);
|
|
|
|
|
if (lf_usage_socket) {
|
|
|
|
|
lf_graph.add_link(*lf_usage_socket, lf_use_input);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
static const bool static_false = false;
|
|
|
|
|
lf_use_input.set_default_value(&static_false);
|
|
|
|
|
}
|
|
|
|
|
lf_graph.add_link(lf_field_socket, lf_field_input);
|
|
|
|
|
return lf_node.output(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Join multiple attributes set into a single attribute set that can be passed into a node.
|
|
|
|
|
*/
|
|
|
|
|
lf::OutputSocket *join_attribute_sets(const Span<lf::OutputSocket *> lf_attribute_set_sockets,
|
|
|
|
|
JoinAttributeSetsCache &cache,
|
|
|
|
|
lf::Graph &lf_graph,
|
|
|
|
|
Set<lf::InputSocket *> &socket_usage_inputs)
|
|
|
|
|
{
|
|
|
|
|
if (lf_attribute_set_sockets.is_empty()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (lf_attribute_set_sockets.size() == 1) {
|
|
|
|
|
return lf_attribute_set_sockets[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<lf::OutputSocket *, 16> key = lf_attribute_set_sockets;
|
|
|
|
|
std::sort(key.begin(), key.end());
|
|
|
|
|
return cache.lookup_or_add_cb(key, [&]() {
|
|
|
|
|
const auto &lazy_function = LazyFunctionForAnonymousAttributeSetJoin::get_cached(
|
|
|
|
|
lf_attribute_set_sockets.size(), scope_);
|
|
|
|
|
lf::Node &lf_node = lf_graph.add_function(lazy_function);
|
|
|
|
|
for (const int i : lf_attribute_set_sockets.index_range()) {
|
|
|
|
|
lf::OutputSocket &lf_attribute_set_socket = *lf_attribute_set_sockets[i];
|
|
|
|
|
lf::InputSocket &lf_use_input = lf_node.input(lazy_function.get_use_input(i));
|
|
|
|
|
|
|
|
|
|
/* Some attribute sets could potentially be set unused in the future based on more dynamic
|
|
|
|
|
* analysis of the node tree. */
|
|
|
|
|
static const bool static_true = true;
|
|
|
|
|
lf_use_input.set_default_value(&static_true);
|
|
|
|
|
|
|
|
|
|
socket_usage_inputs.add(&lf_use_input);
|
|
|
|
|
lf::InputSocket &lf_attribute_set_input = lf_node.input(
|
|
|
|
|
lazy_function.get_attribute_set_input(i));
|
|
|
|
|
lf_graph.add_link(lf_attribute_set_socket, lf_attribute_set_input);
|
|
|
|
|
}
|
|
|
|
|
return &lf_node.output(0);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-20 10:25:41 +02:00
|
|
|
void insert_child_zone_node(const bNodeTreeZone &child_zone, BuildGraphParams &graph_params)
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
{
|
|
|
|
|
const int child_zone_i = child_zone.index;
|
|
|
|
|
ZoneBuildInfo &child_zone_info = zone_build_infos_[child_zone_i];
|
|
|
|
|
lf::FunctionNode &child_zone_node = graph_params.lf_graph.add_function(
|
|
|
|
|
*child_zone_info.lazy_function);
|
|
|
|
|
mapping_->zone_node_map.add_new(&child_zone, &child_zone_node);
|
|
|
|
|
|
2023-09-17 15:13:30 +02:00
|
|
|
for (const int i : child_zone_info.indices.inputs.main.index_range()) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const bNodeSocket &bsocket = child_zone.input_node->input_socket(i);
|
|
|
|
|
lf::InputSocket &lf_input_socket = child_zone_node.input(
|
2023-09-17 15:13:30 +02:00
|
|
|
child_zone_info.indices.inputs.main[i]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::OutputSocket &lf_usage_socket = child_zone_node.output(
|
2023-09-17 15:13:30 +02:00
|
|
|
child_zone_info.indices.outputs.input_usages[i]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_input_socket, &bsocket);
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_input_socket);
|
|
|
|
|
graph_params.usage_by_bsocket.add(&bsocket, &lf_usage_socket);
|
|
|
|
|
}
|
2023-09-17 15:13:30 +02:00
|
|
|
for (const int i : child_zone_info.indices.outputs.main.index_range()) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const bNodeSocket &bsocket = child_zone.output_node->output_socket(i);
|
|
|
|
|
lf::OutputSocket &lf_output_socket = child_zone_node.output(
|
2023-09-17 15:13:30 +02:00
|
|
|
child_zone_info.indices.outputs.main[i]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::InputSocket &lf_usage_input = child_zone_node.input(
|
2023-09-17 15:13:30 +02:00
|
|
|
child_zone_info.indices.inputs.output_usages[i]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_output_socket, &bsocket);
|
|
|
|
|
graph_params.lf_output_by_bsocket.add(&bsocket, &lf_output_socket);
|
|
|
|
|
graph_params.socket_usage_inputs.add(&lf_usage_input);
|
|
|
|
|
if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(&bsocket,
|
2024-01-02 18:12:54 +01:00
|
|
|
nullptr))
|
|
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_graph.add_link(*lf_usage, lf_usage_input);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
static const bool static_false = false;
|
|
|
|
|
lf_usage_input.set_default_value(&static_false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Span<const bNodeLink *> child_border_links = child_zone.border_links;
|
|
|
|
|
for (const int child_border_link_i : child_border_links.index_range()) {
|
|
|
|
|
lf::InputSocket &child_border_link_input = child_zone_node.input(
|
2023-09-17 15:13:30 +02:00
|
|
|
child_zone_info.indices.inputs.border_links[child_border_link_i]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const bNodeLink &link = *child_border_links[child_border_link_i];
|
|
|
|
|
graph_params.lf_input_by_border_link.add(&link, &child_border_link_input);
|
|
|
|
|
lf::OutputSocket &lf_usage = child_zone_node.output(
|
2023-09-17 15:13:30 +02:00
|
|
|
child_zone_info.indices.outputs.border_link_usages[child_border_link_i]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(link.tosock, &child_border_link_input);
|
|
|
|
|
graph_params.usage_by_bsocket.add(link.tosock, &lf_usage);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-17 15:13:30 +02:00
|
|
|
for (const auto item : child_zone_info.indices.inputs.attributes_by_field_source_index.items())
|
|
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const int field_source_index = item.key;
|
|
|
|
|
const int child_zone_input_index = item.value;
|
|
|
|
|
lf::InputSocket &lf_attribute_set_input = child_zone_node.input(child_zone_input_index);
|
|
|
|
|
graph_params.lf_attribute_set_input_by_field_source_index.add(field_source_index,
|
|
|
|
|
&lf_attribute_set_input);
|
|
|
|
|
}
|
2023-09-17 15:13:30 +02:00
|
|
|
for (const auto item :
|
|
|
|
|
child_zone_info.indices.inputs.attributes_by_caller_propagation_index.items())
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
{
|
|
|
|
|
const int caller_propagation_index = item.key;
|
|
|
|
|
const int child_zone_input_index = item.value;
|
|
|
|
|
lf::InputSocket &lf_attribute_set_input = child_zone_node.input(child_zone_input_index);
|
2023-07-11 22:36:10 +02:00
|
|
|
BLI_assert(lf_attribute_set_input.type().is<bke::AnonymousAttributeSet>());
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_attribute_set_input_by_caller_propagation_index.add(caller_propagation_index,
|
|
|
|
|
&lf_attribute_set_input);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_group_input_node(lf::Graph &lf_graph)
|
|
|
|
|
{
|
2023-08-30 12:37:21 +02:00
|
|
|
const Span<const bNodeTreeInterfaceSocket *> interface_inputs = btree_.interface_inputs();
|
|
|
|
|
for (const bNodeTreeInterfaceSocket *interface_input : interface_inputs) {
|
|
|
|
|
const bNodeSocketType *typeinfo = interface_input->socket_typeinfo();
|
2023-10-19 11:48:08 +02:00
|
|
|
lf::GraphInputSocket &lf_socket = lf_graph.add_input(
|
|
|
|
|
*typeinfo->geometry_nodes_cpp_type,
|
|
|
|
|
interface_input->name ? interface_input->name : nullptr);
|
2023-09-17 19:09:45 +02:00
|
|
|
group_input_sockets_.append(&lf_socket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build an output node that just outputs default values in the case when there is no Group
|
|
|
|
|
* Output node in the tree.
|
|
|
|
|
*/
|
|
|
|
|
void build_fallback_output_node(lf::Graph &lf_graph)
|
|
|
|
|
{
|
2023-08-30 12:37:21 +02:00
|
|
|
for (const bNodeTreeInterfaceSocket *interface_output : btree_.interface_outputs()) {
|
|
|
|
|
const bNodeSocketType *typeinfo = interface_output->socket_typeinfo();
|
2023-09-17 13:54:09 +02:00
|
|
|
const CPPType &type = *typeinfo->geometry_nodes_cpp_type;
|
2023-10-19 11:48:08 +02:00
|
|
|
lf::GraphOutputSocket &lf_socket = lf_graph.add_output(
|
|
|
|
|
type, interface_output->name ? interface_output->name : "");
|
2023-09-17 13:54:09 +02:00
|
|
|
lf_socket.set_default_value(type.default_value());
|
2023-09-17 19:09:45 +02:00
|
|
|
standard_group_output_sockets_.append(&lf_socket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void insert_node_in_graph(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
const bNodeType *node_type = bnode.typeinfo;
|
|
|
|
|
if (node_type == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (bnode.is_muted()) {
|
|
|
|
|
this->build_muted_node(bnode, graph_params);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
switch (node_type->type) {
|
|
|
|
|
case NODE_FRAME: {
|
|
|
|
|
/* Ignored. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case NODE_REROUTE: {
|
|
|
|
|
this->build_reroute_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case NODE_GROUP_INPUT: {
|
|
|
|
|
this->handle_group_input_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case NODE_GROUP_OUTPUT: {
|
|
|
|
|
this->build_group_output_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case NODE_CUSTOM_GROUP:
|
|
|
|
|
case NODE_GROUP: {
|
|
|
|
|
this->build_group_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case GEO_NODE_VIEWER: {
|
|
|
|
|
this->build_viewer_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case GEO_NODE_SWITCH: {
|
|
|
|
|
this->build_switch_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-11-22 16:11:32 +01:00
|
|
|
case GEO_NODE_INDEX_SWITCH: {
|
|
|
|
|
this->build_index_switch_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-12-18 13:01:06 +01:00
|
|
|
case GEO_NODE_BAKE: {
|
|
|
|
|
this->build_bake_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-01-26 12:40:01 +01:00
|
|
|
case GEO_NODE_MENU_SWITCH: {
|
|
|
|
|
this->build_menu_switch_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
default: {
|
|
|
|
|
if (node_type->geometry_node_execute) {
|
|
|
|
|
this->build_geometry_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
const NodeMultiFunctions::Item &fn_item = node_multi_functions_.try_get(bnode);
|
|
|
|
|
if (fn_item.fn != nullptr) {
|
|
|
|
|
this->build_multi_function_node(bnode, fn_item, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (node_type == &bke::NodeTypeUndefined) {
|
|
|
|
|
this->build_undefined_node(bnode, graph_params);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Nodes that don't match any of the criteria above are just ignored. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_muted_node(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForMutedNode>(
|
|
|
|
|
bnode, mapping_->lf_index_by_bsocket);
|
|
|
|
|
lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
|
|
|
|
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
|
|
|
|
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
|
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
lf::InputSocket &lf_socket = lf_node.input(lf_index);
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
|
|
|
|
}
|
|
|
|
|
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
|
|
|
|
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
|
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(lf_index);
|
|
|
|
|
graph_params.lf_output_by_bsocket.add_new(bsocket, &lf_socket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->build_muted_node_usages(bnode, graph_params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An input of a muted node is used when any of its internally linked outputs is used.
|
|
|
|
|
*/
|
|
|
|
|
void build_muted_node_usages(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
/* Find all outputs that use a specific input. */
|
|
|
|
|
MultiValueMap<const bNodeSocket *, const bNodeSocket *> outputs_by_input;
|
|
|
|
|
for (const bNodeLink &blink : bnode.internal_links()) {
|
|
|
|
|
outputs_by_input.add(blink.fromsock, blink.tosock);
|
|
|
|
|
}
|
|
|
|
|
for (const auto item : outputs_by_input.items()) {
|
|
|
|
|
const bNodeSocket &input_bsocket = *item.key;
|
|
|
|
|
const Span<const bNodeSocket *> output_bsockets = item.value;
|
|
|
|
|
|
|
|
|
|
/* The input is used if any of the internally linked outputs is used. */
|
|
|
|
|
Vector<lf::OutputSocket *> lf_socket_usages;
|
|
|
|
|
for (const bNodeSocket *output_bsocket : output_bsockets) {
|
|
|
|
|
if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
output_bsocket, nullptr))
|
|
|
|
|
{
|
|
|
|
|
lf_socket_usages.append(lf_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
graph_params.usage_by_bsocket.add(&input_bsocket,
|
|
|
|
|
this->or_socket_usages(lf_socket_usages, graph_params));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_reroute_node(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
const bNodeSocket &input_bsocket = bnode.input_socket(0);
|
|
|
|
|
const bNodeSocket &output_bsocket = bnode.output_socket(0);
|
|
|
|
|
const CPPType *type = get_socket_cpp_type(input_bsocket);
|
|
|
|
|
if (type == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForRerouteNode>(*type);
|
|
|
|
|
lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
|
|
|
|
|
|
|
|
|
lf::InputSocket &lf_input = lf_node.input(0);
|
|
|
|
|
lf::OutputSocket &lf_output = lf_node.output(0);
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(&input_bsocket, &lf_input);
|
|
|
|
|
graph_params.lf_output_by_bsocket.add_new(&output_bsocket, &lf_output);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_input, &input_bsocket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_output, &output_bsocket);
|
|
|
|
|
|
|
|
|
|
if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
&bnode.output_socket(0), nullptr))
|
|
|
|
|
{
|
|
|
|
|
graph_params.usage_by_bsocket.add(&bnode.input_socket(0), lf_usage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void handle_group_input_node(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
for (const int i : btree_.interface_inputs().index_range()) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.output_socket(i);
|
2023-09-17 13:54:09 +02:00
|
|
|
lf::GraphInputSocket &lf_socket = *const_cast<lf::GraphInputSocket *>(
|
2023-09-17 19:09:45 +02:00
|
|
|
group_input_sockets_[i]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_group_output_node(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
2023-09-17 13:54:09 +02:00
|
|
|
Vector<lf::GraphOutputSocket *> lf_graph_outputs;
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
2023-09-17 13:54:09 +02:00
|
|
|
for (const int i : btree_.interface_outputs().index_range()) {
|
|
|
|
|
const bNodeTreeInterfaceSocket &interface_output = *btree_.interface_outputs()[i];
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const bNodeSocket &bsocket = bnode.input_socket(i);
|
2023-09-17 13:54:09 +02:00
|
|
|
const bNodeSocketType *typeinfo = interface_output.socket_typeinfo();
|
|
|
|
|
const CPPType &type = *typeinfo->geometry_nodes_cpp_type;
|
2023-10-19 11:48:08 +02:00
|
|
|
lf::GraphOutputSocket &lf_socket = graph_params.lf_graph.add_output(
|
|
|
|
|
type, interface_output.name ? interface_output.name : "");
|
2023-09-17 13:54:09 +02:00
|
|
|
lf_graph_outputs.append(&lf_socket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (&bnode == btree_.group_output_node()) {
|
2023-09-17 19:09:45 +02:00
|
|
|
standard_group_output_sockets_ = lf_graph_outputs.as_span();
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_group_node(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id);
|
|
|
|
|
if (group_btree == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info =
|
|
|
|
|
ensure_geometry_nodes_lazy_function_graph(*group_btree);
|
|
|
|
|
if (group_lf_graph_info == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForGroupNode>(
|
|
|
|
|
bnode, *group_lf_graph_info, *lf_graph_info_);
|
|
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
for (const int i : bnode.input_sockets().index_range()) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.input_socket(i);
|
2022-09-13 08:44:26 +02:00
|
|
|
BLI_assert(!bsocket.is_multi_input());
|
|
|
|
|
lf::InputSocket &lf_socket = lf_node.input(i);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket);
|
2022-09-13 08:44:26 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
for (const int i : bnode.output_sockets().index_range()) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.output_socket(i);
|
2022-09-13 08:44:26 +02:00
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(i);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket);
|
2022-09-13 08:44:26 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
|
|
|
|
mapping_->group_node_map.add(&bnode, &lf_node);
|
2022-09-20 10:59:12 +02:00
|
|
|
lf_graph_info_->num_inline_nodes_approximate +=
|
|
|
|
|
group_lf_graph_info->num_inline_nodes_approximate;
|
2023-01-05 14:05:30 +01:00
|
|
|
static const bool static_false = false;
|
2023-04-22 13:36:58 +02:00
|
|
|
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
|
|
|
|
{
|
|
|
|
|
const int lf_input_index =
|
|
|
|
|
mapping_->lf_input_index_for_output_bsocket_usage[bsocket->index_in_all_outputs()];
|
|
|
|
|
if (lf_input_index != -1) {
|
|
|
|
|
lf::InputSocket &lf_input = lf_node.input(lf_input_index);
|
|
|
|
|
lf_input.set_default_value(&static_false);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.socket_usage_inputs.add(&lf_input);
|
2023-04-22 13:36:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
/* Keep track of attribute set inputs that need to be populated later. */
|
|
|
|
|
const int lf_input_index = mapping_->lf_input_index_for_attribute_propagation_to_output
|
|
|
|
|
[bsocket->index_in_all_outputs()];
|
|
|
|
|
if (lf_input_index != -1) {
|
|
|
|
|
lf::InputSocket &lf_input = lf_node.input(lf_input_index);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_attribute_set_input_by_output_geometry_bsocket.add(bsocket, &lf_input);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-17 19:09:45 +02:00
|
|
|
this->build_group_node_socket_usage(bnode, lf_node, graph_params, *group_lf_graph_info);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_group_node_socket_usage(const bNode &bnode,
|
|
|
|
|
lf::FunctionNode &lf_group_node,
|
2023-09-17 19:09:45 +02:00
|
|
|
BuildGraphParams &graph_params,
|
|
|
|
|
const GeometryNodesLazyFunctionGraphInfo &group_lf_graph_info)
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
{
|
|
|
|
|
for (const bNodeSocket *input_bsocket : bnode.input_sockets()) {
|
|
|
|
|
const int input_index = input_bsocket->index();
|
|
|
|
|
const InputUsageHint &input_usage_hint =
|
2023-09-17 19:09:45 +02:00
|
|
|
group_lf_graph_info.mapping.group_input_usage_hints[input_index];
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
switch (input_usage_hint.type) {
|
|
|
|
|
case InputUsageHintType::Never: {
|
|
|
|
|
/* Nothing to do. */
|
|
|
|
|
break;
|
2023-04-22 13:36:58 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
case InputUsageHintType::DependsOnOutput: {
|
|
|
|
|
Vector<lf::OutputSocket *> output_usages;
|
|
|
|
|
for (const int i : input_usage_hint.output_dependencies) {
|
|
|
|
|
if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
&bnode.output_socket(i), nullptr))
|
|
|
|
|
{
|
|
|
|
|
output_usages.append(lf_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
graph_params.usage_by_bsocket.add(input_bsocket,
|
|
|
|
|
this->or_socket_usages(output_usages, graph_params));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case InputUsageHintType::DynamicSocket: {
|
|
|
|
|
graph_params.usage_by_bsocket.add(
|
|
|
|
|
input_bsocket,
|
2023-09-17 19:09:45 +02:00
|
|
|
&lf_group_node.output(
|
|
|
|
|
group_lf_graph_info.function.outputs.input_usages[input_index]));
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const bNodeSocket *output_bsocket : bnode.output_sockets()) {
|
|
|
|
|
const int lf_input_index =
|
|
|
|
|
mapping_
|
|
|
|
|
->lf_input_index_for_output_bsocket_usage[output_bsocket->index_in_all_outputs()];
|
|
|
|
|
BLI_assert(lf_input_index >= 0);
|
|
|
|
|
lf::InputSocket &lf_socket = lf_group_node.input(lf_input_index);
|
|
|
|
|
if (lf::OutputSocket *lf_output_is_used = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
output_bsocket, nullptr))
|
|
|
|
|
{
|
|
|
|
|
graph_params.lf_graph.add_link(*lf_output_is_used, lf_socket);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
static const bool static_false = false;
|
|
|
|
|
lf_socket.set_default_value(&static_false);
|
2023-04-22 13:36:58 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_geometry_node(const bNode &bnode, BuildGraphParams &graph_params)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForGeometryNode>(bnode, *lf_graph_info_);
|
|
|
|
|
lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-04-22 16:14:38 +02:00
|
|
|
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
|
|
|
|
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
|
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
lf::InputSocket &lf_socket = lf_node.input(lf_index);
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-04-22 16:14:38 +02:00
|
|
|
if (bsocket->is_multi_input()) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
auto &multi_input_lazy_function = scope_.construct<LazyFunctionForMultiInput>(*bsocket);
|
|
|
|
|
lf::Node &lf_multi_input_node = graph_params.lf_graph.add_function(
|
|
|
|
|
multi_input_lazy_function);
|
|
|
|
|
graph_params.lf_graph.add_link(lf_multi_input_node.output(0), lf_socket);
|
|
|
|
|
graph_params.multi_input_socket_nodes.add_new(bsocket, &lf_multi_input_node);
|
2022-09-13 08:44:26 +02:00
|
|
|
for (lf::InputSocket *lf_multi_input_socket : lf_multi_input_node.inputs()) {
|
2023-04-22 16:14:38 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(lf_multi_input_socket, bsocket);
|
2023-03-09 11:19:59 +01:00
|
|
|
const void *default_value = lf_multi_input_socket->type().default_value();
|
|
|
|
|
lf_multi_input_socket->set_default_value(default_value);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
|
2023-04-22 16:14:38 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-04-22 16:14:38 +02:00
|
|
|
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
|
|
|
|
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
|
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(lf_index);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_output_by_bsocket.add_new(bsocket, &lf_socket);
|
2023-04-22 16:14:38 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-04-22 13:01:00 +02:00
|
|
|
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
|
|
|
|
{
|
|
|
|
|
const int lf_input_index =
|
|
|
|
|
mapping_->lf_input_index_for_output_bsocket_usage[bsocket->index_in_all_outputs()];
|
|
|
|
|
if (lf_input_index != -1) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::InputSocket &lf_input_socket = lf_node.input(lf_input_index);
|
|
|
|
|
if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(bsocket,
|
2024-01-02 18:12:54 +01:00
|
|
|
nullptr))
|
|
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_graph.add_link(*lf_usage, lf_input_socket);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
static const bool static_false = false;
|
|
|
|
|
lf_input_socket.set_default_value(&static_false);
|
|
|
|
|
}
|
|
|
|
|
graph_params.socket_usage_inputs.add_new(&lf_node.input(lf_input_index));
|
2023-04-22 13:01:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
/* Keep track of attribute set inputs that need to be populated later. */
|
|
|
|
|
const int lf_input_index = mapping_->lf_input_index_for_attribute_propagation_to_output
|
|
|
|
|
[bsocket->index_in_all_outputs()];
|
|
|
|
|
if (lf_input_index != -1) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_attribute_set_input_by_output_geometry_bsocket.add(
|
|
|
|
|
bsocket, &lf_node.input(lf_input_index));
|
2023-04-22 13:01:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->build_standard_node_input_socket_usage(bnode, graph_params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_standard_node_input_socket_usage(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
if (bnode.input_sockets().is_empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<lf::OutputSocket *> output_usages;
|
|
|
|
|
for (const bNodeSocket *output_socket : bnode.output_sockets()) {
|
|
|
|
|
if (!output_socket->is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (lf::OutputSocket *is_used_socket = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
output_socket, nullptr))
|
|
|
|
|
{
|
|
|
|
|
output_usages.append_non_duplicates(is_used_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Assume every input is used when any output is used. */
|
|
|
|
|
lf::OutputSocket *lf_usage = this->or_socket_usages(output_usages, graph_params);
|
|
|
|
|
if (lf_usage == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const bNodeSocket *input_socket : bnode.input_sockets()) {
|
|
|
|
|
if (input_socket->is_available()) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(input_socket, lf_usage);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_multi_function_node(const bNode &bnode,
|
|
|
|
|
const NodeMultiFunctions::Item &fn_item,
|
|
|
|
|
BuildGraphParams &graph_params)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForMultiFunctionNode>(
|
2023-04-22 16:14:38 +02:00
|
|
|
bnode, fn_item, mapping_->lf_index_by_bsocket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-04-22 16:14:38 +02:00
|
|
|
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
|
|
|
|
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
|
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
BLI_assert(!bsocket->is_multi_input());
|
|
|
|
|
lf::InputSocket &lf_socket = lf_node.input(lf_index);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
|
2023-04-22 16:14:38 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
2023-04-22 16:14:38 +02:00
|
|
|
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
|
|
|
|
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
|
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(lf_index);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket);
|
2023-04-22 16:14:38 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
|
|
|
|
this->build_standard_node_input_socket_usage(bnode, graph_params);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_viewer_node(const bNode &bnode, BuildGraphParams &graph_params)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForViewerNode>(
|
2023-04-22 16:14:38 +02:00
|
|
|
bnode, mapping_->lf_index_by_bsocket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::FunctionNode &lf_viewer_node = graph_params.lf_graph.add_function(lazy_function);
|
2022-09-13 08:44:26 +02:00
|
|
|
|
2023-04-22 16:14:38 +02:00
|
|
|
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
|
|
|
|
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
|
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::InputSocket &lf_socket = lf_viewer_node.input(lf_index);
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
|
2023-04-22 16:14:38 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-03 14:12:12 +02:00
|
|
|
mapping_->possible_side_effect_node_map.add(&bnode, &lf_viewer_node);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto &usage_lazy_function = scope_.construct<LazyFunctionForViewerInputUsage>(
|
|
|
|
|
lf_viewer_node);
|
|
|
|
|
lf::FunctionNode &lf_usage_node = graph_params.lf_graph.add_function(usage_lazy_function);
|
|
|
|
|
|
|
|
|
|
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
|
|
|
|
if (bsocket->is_available()) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(bsocket, &lf_usage_node.output(0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::FunctionNode *insert_simulation_input_node(const bNodeTree &node_tree,
|
|
|
|
|
const bNode &bnode,
|
|
|
|
|
BuildGraphParams &graph_params)
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
{
|
|
|
|
|
const NodeGeometrySimulationInput *storage = static_cast<const NodeGeometrySimulationInput *>(
|
|
|
|
|
bnode.storage);
|
|
|
|
|
if (node_tree.node_by_id(storage->output_node_id) == nullptr) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
return nullptr;
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<LazyFunction> lazy_function = get_simulation_input_lazy_function(
|
|
|
|
|
node_tree, bnode, *lf_graph_info_);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
|
|
|
|
|
scope_.add(std::move(lazy_function));
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
|
|
|
|
|
for (const int i : bnode.input_sockets().index_range().drop_back(1)) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.input_socket(i);
|
|
|
|
|
lf::InputSocket &lf_socket = lf_node.input(
|
|
|
|
|
mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket);
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
|
|
|
|
for (const int i : bnode.output_sockets().index_range().drop_back(1)) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.output_socket(i);
|
|
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(
|
|
|
|
|
mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_output_by_bsocket.add(&bsocket, &lf_socket);
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
return &lf_node;
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::FunctionNode &insert_simulation_output_node(const bNode &bnode,
|
|
|
|
|
BuildGraphParams &graph_params)
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
{
|
|
|
|
|
std::unique_ptr<LazyFunction> lazy_function = get_simulation_output_lazy_function(
|
|
|
|
|
bnode, *lf_graph_info_);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
|
|
|
|
|
scope_.add(std::move(lazy_function));
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
|
|
|
|
|
for (const int i : bnode.input_sockets().index_range().drop_back(1)) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.input_socket(i);
|
|
|
|
|
lf::InputSocket &lf_socket = lf_node.input(
|
|
|
|
|
mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket);
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
|
|
|
|
for (const int i : bnode.output_sockets().index_range().drop_back(1)) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.output_socket(i);
|
|
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(
|
|
|
|
|
mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_output_by_bsocket.add(&bsocket, &lf_socket);
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
2024-02-27 20:37:52 +01:00
|
|
|
|
|
|
|
|
mapping_->possible_side_effect_node_map.add(&bnode, &lf_node);
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
return lf_node;
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
2023-12-18 13:01:06 +01:00
|
|
|
void build_bake_node(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<LazyFunction> lazy_function = get_bake_lazy_function(bnode, *lf_graph_info_);
|
|
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
|
|
|
|
|
scope_.add(std::move(lazy_function));
|
|
|
|
|
|
|
|
|
|
for (const int i : bnode.input_sockets().index_range().drop_back(1)) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.input_socket(i);
|
|
|
|
|
lf::InputSocket &lf_socket = lf_node.input(
|
|
|
|
|
mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]);
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
|
|
|
|
for (const int i : bnode.output_sockets().index_range().drop_back(1)) {
|
|
|
|
|
const bNodeSocket &bsocket = bnode.output_socket(i);
|
|
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(
|
|
|
|
|
mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]);
|
|
|
|
|
graph_params.lf_output_by_bsocket.add(&bsocket, &lf_socket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mapping_->possible_side_effect_node_map.add(&bnode, &lf_node);
|
|
|
|
|
|
|
|
|
|
this->build_bake_node_socket_usage(bnode, graph_params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_bake_node_socket_usage(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
const LazyFunction &usage_fn = scope_.construct<LazyFunctionForBakeInputsUsage>(bnode);
|
|
|
|
|
lf::FunctionNode &lf_usage_node = graph_params.lf_graph.add_function(usage_fn);
|
|
|
|
|
const int items_num = bnode.input_sockets().size() - 1;
|
|
|
|
|
for (const int i : IndexRange(items_num)) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(&bnode.input_socket(i), &lf_usage_node.output(0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_switch_node(const bNode &bnode, BuildGraphParams &graph_params)
|
2023-03-14 14:09:29 +01:00
|
|
|
{
|
|
|
|
|
std::unique_ptr<LazyFunction> lazy_function = get_switch_node_lazy_function(bnode);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
|
|
|
|
|
scope_.add(std::move(lazy_function));
|
2023-03-14 14:09:29 +01:00
|
|
|
|
2023-12-13 17:33:25 +01:00
|
|
|
for (const int i : bnode.input_sockets().index_range()) {
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(&bnode.input_socket(i), &lf_node.input(i));
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_node.input(i), &bnode.input_socket(i));
|
2023-03-14 14:09:29 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
2023-12-13 17:33:25 +01:00
|
|
|
graph_params.lf_output_by_bsocket.add(&bnode.output_socket(0), &lf_node.output(0));
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_node.output(0), &bnode.output_socket(0));
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->build_switch_node_socket_usage(bnode, graph_params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_switch_node_socket_usage(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
2023-12-13 17:33:25 +01:00
|
|
|
const bNodeSocket &switch_input_bsocket = bnode.input_socket(0);
|
|
|
|
|
const bNodeSocket &false_input_bsocket = bnode.input_socket(1);
|
|
|
|
|
const bNodeSocket &true_input_bsocket = bnode.input_socket(2);
|
|
|
|
|
const bNodeSocket &output_bsocket = bnode.output_socket(0);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::OutputSocket *output_is_used_socket = graph_params.usage_by_bsocket.lookup_default(
|
2023-12-13 17:33:25 +01:00
|
|
|
&output_bsocket, nullptr);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (output_is_used_socket == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-12-13 17:33:25 +01:00
|
|
|
graph_params.usage_by_bsocket.add(&switch_input_bsocket, output_is_used_socket);
|
|
|
|
|
if (switch_input_bsocket.is_directly_linked()) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/* The condition input is dynamic, so the usage of the other inputs is as well. */
|
|
|
|
|
static const LazyFunctionForSwitchSocketUsage switch_socket_usage_fn;
|
|
|
|
|
lf::Node &lf_node = graph_params.lf_graph.add_function(switch_socket_usage_fn);
|
2023-12-13 17:33:25 +01:00
|
|
|
graph_params.lf_inputs_by_bsocket.add(&switch_input_bsocket, &lf_node.input(0));
|
|
|
|
|
graph_params.usage_by_bsocket.add(&false_input_bsocket, &lf_node.output(0));
|
|
|
|
|
graph_params.usage_by_bsocket.add(&true_input_bsocket, &lf_node.output(1));
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-13 17:33:25 +01:00
|
|
|
if (switch_input_bsocket.default_value_typed<bNodeSocketValueBoolean>()->value) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(&true_input_bsocket, output_is_used_socket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-13 17:33:25 +01:00
|
|
|
graph_params.usage_by_bsocket.add(&false_input_bsocket, output_is_used_socket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-14 14:09:29 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-22 16:11:32 +01:00
|
|
|
void build_index_switch_node(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
2024-01-08 14:04:31 -05:00
|
|
|
std::unique_ptr<LazyFunction> lazy_function = get_index_switch_node_lazy_function(
|
|
|
|
|
bnode, *lf_graph_info_);
|
2023-11-22 16:11:32 +01:00
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
|
|
|
|
|
scope_.add(std::move(lazy_function));
|
|
|
|
|
|
|
|
|
|
for (const int i : bnode.input_sockets().drop_back(1).index_range()) {
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(&bnode.input_socket(i), &lf_node.input(i));
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_node.input(i), &bnode.input_socket(i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
graph_params.lf_output_by_bsocket.add(&bnode.output_socket(0), &lf_node.output(0));
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_node.output(0), &bnode.output_socket(0));
|
|
|
|
|
|
|
|
|
|
this->build_index_switch_node_socket_usage(bnode, graph_params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_index_switch_node_socket_usage(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
const bNodeSocket &index_socket = bnode.input_socket(0);
|
|
|
|
|
const int items_num = bnode.input_sockets().size() - 1;
|
|
|
|
|
|
|
|
|
|
lf::OutputSocket *output_is_used = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
&bnode.output_socket(0), nullptr);
|
|
|
|
|
if (output_is_used == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
graph_params.usage_by_bsocket.add(&index_socket, output_is_used);
|
|
|
|
|
if (index_socket.is_directly_linked()) {
|
|
|
|
|
/* The condition input is dynamic, so the usage of the other inputs is as well. */
|
|
|
|
|
auto usage_fn = std::make_unique<LazyFunctionForIndexSwitchSocketUsage>(bnode);
|
|
|
|
|
lf::Node &lf_node = graph_params.lf_graph.add_function(*usage_fn);
|
|
|
|
|
scope_.add(std::move(usage_fn));
|
|
|
|
|
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(&index_socket, &lf_node.input(0));
|
|
|
|
|
for (const int i : IndexRange(items_num)) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(&bnode.input_socket(i + 1), &lf_node.output(i));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const int index = index_socket.default_value_typed<bNodeSocketValueInt>()->value;
|
|
|
|
|
if (IndexRange(items_num).contains(index)) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(&bnode.input_socket(index + 1), output_is_used);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-26 12:40:01 +01:00
|
|
|
void build_menu_switch_node(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<LazyFunction> lazy_function = get_menu_switch_node_lazy_function(
|
|
|
|
|
bnode, *lf_graph_info_);
|
|
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
|
|
|
|
|
scope_.add(std::move(lazy_function));
|
|
|
|
|
|
|
|
|
|
int input_index = 0;
|
|
|
|
|
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
|
|
|
|
if (bsocket->is_available()) {
|
|
|
|
|
lf::InputSocket &lf_socket = lf_node.input(input_index);
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
|
|
|
|
input_index++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
|
|
|
|
if (bsocket->is_available()) {
|
|
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(0);
|
|
|
|
|
graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket);
|
|
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->build_menu_switch_node_socket_usage(bnode, graph_params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_menu_switch_node_socket_usage(const bNode &bnode, BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
const NodeMenuSwitch &storage = *static_cast<NodeMenuSwitch *>(bnode.storage);
|
|
|
|
|
const NodeEnumDefinition &enum_def = storage.enum_definition;
|
|
|
|
|
|
|
|
|
|
const bNodeSocket *switch_input_bsocket = bnode.input_sockets()[0];
|
|
|
|
|
Vector<const bNodeSocket *> input_bsockets(enum_def.items_num);
|
|
|
|
|
for (const int i : IndexRange(enum_def.items_num)) {
|
|
|
|
|
input_bsockets[i] = bnode.input_sockets()[i + 1];
|
|
|
|
|
}
|
|
|
|
|
const bNodeSocket *output_bsocket = bnode.output_sockets()[0];
|
|
|
|
|
|
|
|
|
|
lf::OutputSocket *output_is_used_socket = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
output_bsocket, nullptr);
|
|
|
|
|
if (output_is_used_socket == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
graph_params.usage_by_bsocket.add(switch_input_bsocket, output_is_used_socket);
|
|
|
|
|
if (switch_input_bsocket->is_directly_linked()) {
|
|
|
|
|
/* The condition input is dynamic, so the usage of the other inputs is as well. */
|
|
|
|
|
std::unique_ptr<LazyFunction> lazy_function =
|
|
|
|
|
get_menu_switch_node_socket_usage_lazy_function(bnode);
|
|
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function);
|
|
|
|
|
scope_.add(std::move(lazy_function));
|
|
|
|
|
|
|
|
|
|
graph_params.lf_inputs_by_bsocket.add(switch_input_bsocket, &lf_node.input(0));
|
|
|
|
|
for (const int i : IndexRange(enum_def.items_num)) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(input_bsockets[i], &lf_node.output(i));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const int condition =
|
|
|
|
|
switch_input_bsocket->default_value_typed<bNodeSocketValueMenu>()->value;
|
|
|
|
|
for (const int i : IndexRange(enum_def.items_num)) {
|
|
|
|
|
const NodeEnumItem &enum_item = enum_def.items()[i];
|
|
|
|
|
if (enum_item.identifier == condition) {
|
|
|
|
|
graph_params.usage_by_bsocket.add(input_bsockets[i], output_is_used_socket);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_undefined_node(const bNode &bnode, BuildGraphParams &graph_params)
|
2022-09-16 12:54:46 -05:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForUndefinedNode>(
|
2023-04-22 16:14:38 +02:00
|
|
|
bnode, mapping_->lf_index_by_bsocket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
2022-09-16 12:54:46 -05:00
|
|
|
|
2023-04-22 16:14:38 +02:00
|
|
|
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
|
|
|
|
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
2023-05-16 11:04:14 -04:00
|
|
|
if (lf_index == -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-04-22 16:14:38 +02:00
|
|
|
lf::OutputSocket &lf_socket = lf_node.output(lf_index);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket);
|
2023-04-22 16:14:38 +02:00
|
|
|
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
2022-09-16 12:54:46 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
struct TypeWithLinks {
|
2023-12-17 14:00:07 +01:00
|
|
|
const bNodeSocketType *typeinfo;
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Vector<const bNodeLink *> links;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void insert_links_from_socket(const bNodeSocket &from_bsocket,
|
|
|
|
|
lf::OutputSocket &from_lf_socket,
|
|
|
|
|
BuildGraphParams &graph_params)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (bke::nodeIsDanglingReroute(&btree_, &from_bsocket.owner_node())) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-17 14:00:07 +01:00
|
|
|
const bNodeSocketType &from_typeinfo = *from_bsocket.typeinfo;
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/* Group available target sockets by type so that they can be handled together. */
|
|
|
|
|
const Vector<TypeWithLinks> types_with_links = this->group_link_targets_by_type(from_bsocket);
|
|
|
|
|
|
|
|
|
|
for (const TypeWithLinks &type_with_links : types_with_links) {
|
2023-12-17 14:00:07 +01:00
|
|
|
if (type_with_links.typeinfo == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (type_with_links.typeinfo->geometry_nodes_cpp_type == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const bNodeSocketType &to_typeinfo = *type_with_links.typeinfo;
|
|
|
|
|
const CPPType &to_type = *to_typeinfo.geometry_nodes_cpp_type;
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const Span<const bNodeLink *> links = type_with_links.links;
|
|
|
|
|
|
|
|
|
|
lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary(
|
2023-12-17 14:00:07 +01:00
|
|
|
from_lf_socket, from_typeinfo, to_typeinfo, graph_params.lf_graph);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
|
|
|
|
for (const bNodeLink *link : links) {
|
|
|
|
|
const Vector<lf::InputSocket *> lf_link_targets = this->find_link_targets(*link,
|
|
|
|
|
graph_params);
|
|
|
|
|
if (converted_from_lf_socket == nullptr) {
|
|
|
|
|
const void *default_value = to_type.default_value();
|
|
|
|
|
for (lf::InputSocket *to_lf_socket : lf_link_targets) {
|
|
|
|
|
to_lf_socket->set_default_value(default_value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (lf::InputSocket *to_lf_socket : lf_link_targets) {
|
|
|
|
|
graph_params.lf_graph.add_link(*converted_from_lf_socket, *to_lf_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Vector<TypeWithLinks> group_link_targets_by_type(const bNodeSocket &from_bsocket)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
|
|
|
|
const Span<const bNodeLink *> links_from_bsocket = from_bsocket.directly_linked_links();
|
|
|
|
|
Vector<TypeWithLinks> types_with_links;
|
|
|
|
|
for (const bNodeLink *link : links_from_bsocket) {
|
|
|
|
|
if (link->is_muted()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-09-20 13:21:03 +02:00
|
|
|
if (!link->is_available()) {
|
2022-09-13 08:44:26 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2022-09-20 13:21:03 +02:00
|
|
|
const bNodeSocket &to_bsocket = *link->tosock;
|
2022-09-13 08:44:26 +02:00
|
|
|
bool inserted = false;
|
|
|
|
|
for (TypeWithLinks &types_with_links : types_with_links) {
|
2023-12-17 14:00:07 +01:00
|
|
|
if (types_with_links.typeinfo == to_bsocket.typeinfo) {
|
2022-09-13 08:44:26 +02:00
|
|
|
types_with_links.links.append(link);
|
|
|
|
|
inserted = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (inserted) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-12-17 14:00:07 +01:00
|
|
|
types_with_links.append({to_bsocket.typeinfo, {link}});
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
return types_with_links;
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
Vector<lf::InputSocket *> find_link_targets(const bNodeLink &link,
|
|
|
|
|
const BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
if (lf::InputSocket *lf_input_socket = graph_params.lf_input_by_border_link.lookup_default(
|
|
|
|
|
&link, nullptr))
|
|
|
|
|
{
|
|
|
|
|
return {lf_input_socket};
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const bNodeSocket &to_bsocket = *link.tosock;
|
|
|
|
|
if (to_bsocket.is_multi_input()) {
|
|
|
|
|
/* TODO: Cache this index on the link. */
|
|
|
|
|
int link_index = 0;
|
|
|
|
|
for (const bNodeLink *multi_input_link : to_bsocket.directly_linked_links()) {
|
|
|
|
|
if (multi_input_link == &link) {
|
|
|
|
|
break;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (multi_input_link->is_muted() || !multi_input_link->fromsock->is_available() ||
|
|
|
|
|
bke::nodeIsDanglingReroute(&btree_, multi_input_link->fromnode))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
link_index++;
|
|
|
|
|
}
|
|
|
|
|
if (to_bsocket.owner_node().is_muted()) {
|
|
|
|
|
if (link_index == 0) {
|
|
|
|
|
return Vector<lf::InputSocket *>(graph_params.lf_inputs_by_bsocket.lookup(&to_bsocket));
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
lf::Node *multi_input_lf_node = graph_params.multi_input_socket_nodes.lookup_default(
|
|
|
|
|
&to_bsocket, nullptr);
|
|
|
|
|
if (multi_input_lf_node == nullptr) {
|
|
|
|
|
return {};
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
return {&multi_input_lf_node->input(link_index)};
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
else {
|
|
|
|
|
return Vector<lf::InputSocket *>(graph_params.lf_inputs_by_bsocket.lookup(&to_bsocket));
|
|
|
|
|
}
|
|
|
|
|
return {};
|
2022-09-13 08:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-16 13:16:27 +01:00
|
|
|
lf::OutputSocket *insert_type_conversion_if_necessary(lf::OutputSocket &from_socket,
|
2023-12-17 14:00:07 +01:00
|
|
|
const bNodeSocketType &from_typeinfo,
|
|
|
|
|
const bNodeSocketType &to_typeinfo,
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::Graph &lf_graph)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
2023-12-17 14:00:07 +01:00
|
|
|
if (from_typeinfo.type == to_typeinfo.type) {
|
2022-09-13 08:44:26 +02:00
|
|
|
return &from_socket;
|
|
|
|
|
}
|
2023-12-17 14:00:07 +01:00
|
|
|
if (from_typeinfo.base_cpp_type && to_typeinfo.base_cpp_type) {
|
|
|
|
|
if (conversions_->is_convertible(*from_typeinfo.base_cpp_type, *to_typeinfo.base_cpp_type)) {
|
2022-09-13 08:44:26 +02:00
|
|
|
const MultiFunction &multi_fn = *conversions_->get_conversion_multi_function(
|
2023-12-17 14:00:07 +01:00
|
|
|
mf::DataType::ForSingle(*from_typeinfo.base_cpp_type),
|
|
|
|
|
mf::DataType::ForSingle(*to_typeinfo.base_cpp_type));
|
|
|
|
|
auto &fn = scope_.construct<LazyFunctionForMultiFunctionConversion>(multi_fn);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::Node &conversion_node = lf_graph.add_function(fn);
|
|
|
|
|
lf_graph.add_link(from_socket, conversion_node.input(0));
|
2022-09-13 08:44:26 +02:00
|
|
|
return &conversion_node.output(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void add_default_inputs(BuildGraphParams &graph_params)
|
2022-09-13 08:44:26 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
for (auto item : graph_params.lf_inputs_by_bsocket.items()) {
|
2022-09-13 08:44:26 +02:00
|
|
|
const bNodeSocket &bsocket = *item.key;
|
|
|
|
|
const Span<lf::InputSocket *> lf_sockets = item.value;
|
|
|
|
|
for (lf::InputSocket *lf_socket : lf_sockets) {
|
|
|
|
|
if (lf_socket->origin() != nullptr) {
|
|
|
|
|
/* Is linked already. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
this->add_default_input(bsocket, *lf_socket, graph_params);
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
|
|
|
|
|
void add_default_input(const bNodeSocket &input_bsocket,
|
|
|
|
|
lf::InputSocket &input_lf_socket,
|
|
|
|
|
BuildGraphParams &graph_params)
|
|
|
|
|
{
|
|
|
|
|
if (this->try_add_implicit_input(input_bsocket, input_lf_socket, graph_params)) {
|
2023-01-05 14:05:30 +01:00
|
|
|
return;
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
GMutablePointer value = get_socket_default_value(scope_.linear_allocator(), input_bsocket);
|
|
|
|
|
if (value.get() == nullptr) {
|
|
|
|
|
/* Not possible to add a default value. */
|
|
|
|
|
return;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
input_lf_socket.set_default_value(value.get());
|
|
|
|
|
if (!value.type()->is_trivially_destructible()) {
|
|
|
|
|
scope_.add_destruct_call([value]() mutable { value.destruct(); });
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
bool try_add_implicit_input(const bNodeSocket &input_bsocket,
|
|
|
|
|
lf::InputSocket &input_lf_socket,
|
|
|
|
|
BuildGraphParams &graph_params)
|
2023-01-05 14:05:30 +01:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const bNode &bnode = input_bsocket.owner_node();
|
|
|
|
|
const SocketDeclaration *socket_decl = input_bsocket.runtime->declaration;
|
|
|
|
|
if (socket_decl == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (socket_decl->input_field_type != InputSocketFieldType::Implicit) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-10-12 14:04:44 +02:00
|
|
|
const ImplicitInputValueFn *implicit_input_fn = socket_decl->implicit_input_fn.get();
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (implicit_input_fn == nullptr) {
|
|
|
|
|
return false;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
std::function<void(void *)> init_fn = [&bnode, implicit_input_fn](void *r_value) {
|
|
|
|
|
(*implicit_input_fn)(bnode, r_value);
|
|
|
|
|
};
|
|
|
|
|
const CPPType &type = input_lf_socket.type();
|
|
|
|
|
auto &lazy_function = scope_.construct<LazyFunctionForImplicitInput>(type, std::move(init_fn));
|
|
|
|
|
lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
|
|
|
|
graph_params.lf_graph.add_link(lf_node.output(0), input_lf_socket);
|
|
|
|
|
return true;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/**
|
|
|
|
|
* Every output geometry socket that may propagate attributes has to know which attributes
|
|
|
|
|
* should be propagated. Therefore, every one of these outputs gets a corresponding attribute
|
|
|
|
|
* set input.
|
|
|
|
|
*/
|
|
|
|
|
void build_attribute_propagation_input_node(lf::Graph &lf_graph)
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
const aal::RelationsInNode &tree_relations =
|
|
|
|
|
btree_.runtime->anonymous_attribute_inferencing->tree_relations;
|
|
|
|
|
Vector<int> output_indices;
|
|
|
|
|
for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) {
|
|
|
|
|
output_indices.append_non_duplicates(relation.to_geometry_output);
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
2023-09-17 13:54:09 +02:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
for (const int i : output_indices.index_range()) {
|
|
|
|
|
const int output_index = output_indices[i];
|
2023-10-19 11:48:08 +02:00
|
|
|
const char *name = btree_.interface_outputs()[output_index]->name;
|
2023-09-17 13:54:09 +02:00
|
|
|
lf::GraphInputSocket &lf_socket = lf_graph.add_input(
|
|
|
|
|
CPPType::get<bke::AnonymousAttributeSet>(),
|
2023-10-19 11:48:08 +02:00
|
|
|
StringRef("Propagate: ") + (name ? name : ""));
|
2023-09-17 19:09:45 +02:00
|
|
|
attribute_set_by_geometry_output_.add(output_index, &lf_socket);
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
}
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/**
|
|
|
|
|
* Combine multiple socket usages with a logical or. Inserts a new node for that purpose if
|
|
|
|
|
* necessary.
|
|
|
|
|
*/
|
|
|
|
|
lf::OutputSocket *or_socket_usages(MutableSpan<lf::OutputSocket *> usages,
|
|
|
|
|
BuildGraphParams &graph_params)
|
2023-01-05 14:05:30 +01:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (usages.is_empty()) {
|
|
|
|
|
return nullptr;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (usages.size() == 1) {
|
|
|
|
|
return usages[0];
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
std::sort(usages.begin(), usages.end());
|
|
|
|
|
return graph_params.socket_usages_combination_cache.lookup_or_add_cb_as(usages, [&]() {
|
|
|
|
|
auto &logical_or_fn = scope_.construct<LazyFunctionForLogicalOr>(usages.size());
|
|
|
|
|
lf::Node &logical_or_node = graph_params.lf_graph.add_function(logical_or_fn);
|
2023-01-05 14:05:30 +01:00
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
for (const int i : usages.index_range()) {
|
|
|
|
|
graph_params.lf_graph.add_link(*usages[i], logical_or_node.input(i));
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
return &logical_or_node.output(0);
|
|
|
|
|
});
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_output_socket_usages(const bNode &bnode, BuildGraphParams &graph_params)
|
2023-01-05 14:05:30 +01:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/* Output sockets are used when any of their linked inputs are used. */
|
|
|
|
|
for (const bNodeSocket *socket : bnode.output_sockets()) {
|
|
|
|
|
if (!socket->is_available()) {
|
2023-01-05 14:05:30 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/* Determine when linked target sockets are used. */
|
|
|
|
|
Vector<lf::OutputSocket *> target_usages;
|
|
|
|
|
for (const bNodeLink *link : socket->directly_linked_links()) {
|
|
|
|
|
if (!link->is_used()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const bNodeSocket &target_socket = *link->tosock;
|
|
|
|
|
if (lf::OutputSocket *is_used_socket = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
&target_socket, nullptr))
|
|
|
|
|
{
|
|
|
|
|
target_usages.append_non_duplicates(is_used_socket);
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
/* Combine target socket usages into the usage of the current socket. */
|
|
|
|
|
graph_params.usage_by_bsocket.add(socket,
|
|
|
|
|
this->or_socket_usages(target_usages, graph_params));
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void build_group_input_usages(BuildGraphParams &graph_params)
|
2023-01-05 14:05:30 +01:00
|
|
|
{
|
|
|
|
|
const Span<const bNode *> group_input_nodes = btree_.group_input_nodes();
|
|
|
|
|
for (const int i : btree_.interface_inputs().index_range()) {
|
|
|
|
|
Vector<lf::OutputSocket *> target_usages;
|
|
|
|
|
for (const bNode *group_input_node : group_input_nodes) {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default(
|
|
|
|
|
&group_input_node->output_socket(i), nullptr))
|
2023-01-05 14:05:30 +01:00
|
|
|
{
|
|
|
|
|
target_usages.append_non_duplicates(lf_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf::OutputSocket *lf_socket = this->or_socket_usages(target_usages, graph_params);
|
2023-01-05 14:05:30 +01:00
|
|
|
lf::InputSocket *lf_group_output = const_cast<lf::InputSocket *>(
|
2023-09-17 19:09:45 +02:00
|
|
|
group_input_usage_sockets_[i]);
|
2023-01-05 14:05:30 +01:00
|
|
|
InputUsageHint input_usage_hint;
|
|
|
|
|
if (lf_socket == nullptr) {
|
|
|
|
|
static const bool static_false = false;
|
|
|
|
|
lf_group_output->set_default_value(&static_false);
|
|
|
|
|
input_usage_hint.type = InputUsageHintType::Never;
|
|
|
|
|
}
|
|
|
|
|
else {
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
graph_params.lf_graph.add_link(*lf_socket, *lf_group_output);
|
2023-09-17 13:54:09 +02:00
|
|
|
if (lf_socket->node().is_interface()) {
|
2023-01-05 14:05:30 +01:00
|
|
|
/* Can support slightly more complex cases where it depends on more than one output in
|
|
|
|
|
* the future. */
|
|
|
|
|
input_usage_hint.type = InputUsageHintType::DependsOnOutput;
|
|
|
|
|
input_usage_hint.output_dependencies = {
|
2023-09-17 19:09:45 +02:00
|
|
|
group_output_used_sockets_.first_index_of(lf_socket)};
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
input_usage_hint.type = InputUsageHintType::DynamicSocket;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lf_graph_info_->mapping.group_input_usage_hints.append(std::move(input_usage_hint));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
* By depending on "the future" (whether a specific socket is used in the future), it is
|
|
|
|
|
* possible to introduce cycles in the graph. This function finds those cycles and breaks them
|
|
|
|
|
* by removing specific links.
|
2023-01-05 14:05:30 +01:00
|
|
|
*
|
|
|
|
|
* Example for a cycle: There is a `Distribute Points on Faces` node and its `Normal` output is
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
* only used when the number of generated points is larger than 1000 because of some switch
|
|
|
|
|
* node later in the tree. In this case, to know whether the `Normal` output is needed, one
|
|
|
|
|
* first has to compute the points, but for that one has to know whether the normal information
|
|
|
|
|
* has to be added to the points. The fix is to always add the normal information in this case.
|
2023-01-05 14:05:30 +01:00
|
|
|
*/
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
void fix_link_cycles(lf::Graph &lf_graph, const Set<lf::InputSocket *> &socket_usage_inputs)
|
2023-01-05 14:05:30 +01:00
|
|
|
{
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf_graph.update_socket_indices();
|
|
|
|
|
const int sockets_num = lf_graph.socket_num();
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
struct SocketState {
|
|
|
|
|
bool done = false;
|
|
|
|
|
bool in_stack = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Array<SocketState> socket_states(sockets_num);
|
|
|
|
|
|
2023-01-20 14:38:09 +01:00
|
|
|
Vector<lf::Socket *> lf_sockets_to_check;
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
for (lf::Node *lf_node : lf_graph.nodes()) {
|
2023-01-05 14:05:30 +01:00
|
|
|
if (lf_node->is_function()) {
|
|
|
|
|
for (lf::OutputSocket *lf_socket : lf_node->outputs()) {
|
|
|
|
|
if (lf_socket->targets().is_empty()) {
|
2023-01-20 14:38:09 +01:00
|
|
|
lf_sockets_to_check.append(lf_socket);
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (lf_node->outputs().is_empty()) {
|
|
|
|
|
for (lf::InputSocket *lf_socket : lf_node->inputs()) {
|
2023-01-20 14:38:09 +01:00
|
|
|
lf_sockets_to_check.append(lf_socket);
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Vector<lf::Socket *> lf_socket_stack;
|
|
|
|
|
while (!lf_sockets_to_check.is_empty()) {
|
2023-01-20 14:38:09 +01:00
|
|
|
lf::Socket *lf_inout_socket = lf_sockets_to_check.last();
|
2023-01-05 14:05:30 +01:00
|
|
|
lf::Node &lf_node = lf_inout_socket->node();
|
|
|
|
|
SocketState &state = socket_states[lf_inout_socket->index_in_graph()];
|
2023-01-20 14:38:09 +01:00
|
|
|
|
|
|
|
|
if (!state.in_stack) {
|
|
|
|
|
lf_socket_stack.append(lf_inout_socket);
|
|
|
|
|
state.in_stack = true;
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
Vector<lf::Socket *, 16> lf_origin_sockets;
|
|
|
|
|
if (lf_inout_socket->is_input()) {
|
|
|
|
|
lf::InputSocket &lf_input_socket = lf_inout_socket->as_input();
|
|
|
|
|
if (lf::OutputSocket *lf_origin_socket = lf_input_socket.origin()) {
|
|
|
|
|
lf_origin_sockets.append(lf_origin_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
lf::OutputSocket &lf_output_socket = lf_inout_socket->as_output();
|
|
|
|
|
if (lf_node.is_function()) {
|
|
|
|
|
lf::FunctionNode &lf_function_node = static_cast<lf::FunctionNode &>(lf_node);
|
|
|
|
|
const lf::LazyFunction &fn = lf_function_node.function();
|
|
|
|
|
fn.possible_output_dependencies(
|
|
|
|
|
lf_output_socket.index(), [&](const Span<int> input_indices) {
|
|
|
|
|
for (const int input_index : input_indices) {
|
|
|
|
|
lf_origin_sockets.append(&lf_node.input(input_index));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool pushed_socket = false;
|
2023-01-20 14:38:09 +01:00
|
|
|
bool detected_cycle = false;
|
2023-01-05 14:05:30 +01:00
|
|
|
for (lf::Socket *lf_origin_socket : lf_origin_sockets) {
|
|
|
|
|
if (socket_states[lf_origin_socket->index_in_graph()].in_stack) {
|
2023-01-20 14:38:09 +01:00
|
|
|
/* A cycle has been detected. The cycle is broken by removing a link and replacing it
|
|
|
|
|
* with a constant "true" input. This can only affect inputs which determine whether a
|
|
|
|
|
* specific value is used. Therefore, setting it to a constant true can result in more
|
|
|
|
|
* computation later, but does not change correctness.
|
|
|
|
|
*
|
|
|
|
|
* After the cycle is broken, the cycle-detection is "rolled back" to the socket where
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
* the first socket of the cycle was found. This is necessary in case another cycle
|
|
|
|
|
* goes through this socket. */
|
2023-01-20 14:38:09 +01:00
|
|
|
|
|
|
|
|
detected_cycle = true;
|
|
|
|
|
const int index_in_socket_stack = lf_socket_stack.first_index_of(lf_origin_socket);
|
|
|
|
|
const int index_in_sockets_to_check = lf_sockets_to_check.first_index_of(
|
|
|
|
|
lf_origin_socket);
|
2023-01-05 14:05:30 +01:00
|
|
|
const Span<lf::Socket *> cycle = lf_socket_stack.as_span().drop_front(
|
2023-01-20 14:38:09 +01:00
|
|
|
index_in_socket_stack);
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
bool broke_cycle = false;
|
|
|
|
|
for (lf::Socket *lf_cycle_socket : cycle) {
|
|
|
|
|
if (lf_cycle_socket->is_input() &&
|
2024-01-02 18:12:54 +01:00
|
|
|
socket_usage_inputs.contains(&lf_cycle_socket->as_input()))
|
|
|
|
|
{
|
2023-01-05 14:05:30 +01:00
|
|
|
lf::InputSocket &lf_cycle_input_socket = lf_cycle_socket->as_input();
|
Geometry Nodes: make evaluation and logging system aware of zones
This refactors how a geometry nodes node tree is converted to a lazy-function
graph. Previously, all nodes were inserted into a single graph. This was fine
because every node was evaluated at most once per node group evaluation.
However, loops (#108896) break this assumption since now nodes may be
evaluated multiple times and thus a single flat graph does not work anymore.
Now, a separate lazy-function is build for every zone which gives us much
more flexibility for what can happen in a zone. Right now, the change only
applies to simulation zones since that's the only kind of zone we have.
Technically, those zones could be inlined, but turning them into a separate
lazy-function also does not hurt and makes it possible to test this refactor
without implementing loops first. Also, having them as separate functions
might help in the future if we integrate a substep loop directly into the
simulation zone.
The most tricky part here is to just link everything up correctly, especially
with respect to deterministic anonymous attribute lifetimes. Fortunately,
correctness can be checked visually by looking at the generated graphs.
The logging/viewer system also had to be refactored a bit, because now there
can be multiple different `ComputeContext` in a single node tree. Each zone
is in a separate `ComputeContext`. To make it work, the `ViewerPath` system
now explicitly supports zones and drawing code will look up the right logger
for showing inspection data.
No functional changes are expected, except that the spreadsheet now shows
"Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
|
|
|
lf_graph.clear_origin(lf_cycle_input_socket);
|
2023-01-05 14:05:30 +01:00
|
|
|
static const bool static_true = true;
|
|
|
|
|
lf_cycle_input_socket.set_default_value(&static_true);
|
|
|
|
|
broke_cycle = true;
|
|
|
|
|
}
|
2023-01-20 14:38:09 +01:00
|
|
|
/* This is actually removed from the stack when it is resized below. */
|
|
|
|
|
SocketState &lf_cycle_socket_state = socket_states[lf_cycle_socket->index_in_graph()];
|
|
|
|
|
lf_cycle_socket_state.in_stack = false;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
if (!broke_cycle) {
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
}
|
2023-01-20 14:38:09 +01:00
|
|
|
/* Roll back algorithm by removing the sockets that corresponded to the cycle from the
|
|
|
|
|
* stacks. */
|
|
|
|
|
lf_socket_stack.resize(index_in_socket_stack);
|
|
|
|
|
/* The +1 is there so that the socket itself is not removed. */
|
|
|
|
|
lf_sockets_to_check.resize(index_in_sockets_to_check + 1);
|
|
|
|
|
break;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
else if (!socket_states[lf_origin_socket->index_in_graph()].done) {
|
2023-01-20 14:38:09 +01:00
|
|
|
lf_sockets_to_check.append(lf_origin_socket);
|
2023-01-05 14:05:30 +01:00
|
|
|
pushed_socket = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-20 14:38:09 +01:00
|
|
|
if (detected_cycle) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
if (pushed_socket) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state.done = true;
|
|
|
|
|
state.in_stack = false;
|
2023-01-20 14:38:09 +01:00
|
|
|
lf_sockets_to_check.pop_last();
|
2023-01-05 14:05:30 +01:00
|
|
|
lf_socket_stack.pop_last();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph(
|
|
|
|
|
const bNodeTree &btree)
|
|
|
|
|
{
|
|
|
|
|
btree.ensure_topology_cache();
|
2023-09-14 14:13:07 +02:00
|
|
|
btree.ensure_interface_cache();
|
2022-09-20 13:21:03 +02:00
|
|
|
if (btree.has_available_link_cycle()) {
|
2022-09-13 08:44:26 +02:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
2023-06-26 14:33:04 +02:00
|
|
|
const bNodeTreeZones *tree_zones = btree.zones();
|
|
|
|
|
if (tree_zones == nullptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2023-07-11 22:36:10 +02:00
|
|
|
for (const std::unique_ptr<bNodeTreeZone> &zone : tree_zones->zones) {
|
|
|
|
|
if (zone->input_node == nullptr || zone->output_node == nullptr) {
|
|
|
|
|
/* Simulations and repeats need input and output nodes. */
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-03 19:15:06 +02:00
|
|
|
if (const ID *id_orig = DEG_get_original_id(const_cast<ID *>(&btree.id))) {
|
|
|
|
|
if (id_orig->tag & LIB_TAG_MISSING) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-30 12:37:21 +02:00
|
|
|
for (const bNodeTreeInterfaceSocket *interface_bsocket : btree.interface_inputs()) {
|
|
|
|
|
const bNodeSocketType *typeinfo = interface_bsocket->socket_typeinfo();
|
|
|
|
|
if (typeinfo->geometry_nodes_cpp_type == nullptr) {
|
2022-12-16 18:30:47 +01:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-30 12:37:21 +02:00
|
|
|
for (const bNodeTreeInterfaceSocket *interface_bsocket : btree.interface_outputs()) {
|
|
|
|
|
const bNodeSocketType *typeinfo = interface_bsocket->socket_typeinfo();
|
|
|
|
|
if (typeinfo->geometry_nodes_cpp_type == nullptr) {
|
2022-12-16 18:30:47 +01:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
std::unique_ptr<GeometryNodesLazyFunctionGraphInfo> &lf_graph_info_ptr =
|
|
|
|
|
btree.runtime->geometry_nodes_lazy_function_graph_info;
|
|
|
|
|
|
|
|
|
|
if (lf_graph_info_ptr) {
|
|
|
|
|
return lf_graph_info_ptr.get();
|
|
|
|
|
}
|
|
|
|
|
std::lock_guard lock{btree.runtime->geometry_nodes_lazy_function_graph_info_mutex};
|
|
|
|
|
if (lf_graph_info_ptr) {
|
|
|
|
|
return lf_graph_info_ptr.get();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto lf_graph_info = std::make_unique<GeometryNodesLazyFunctionGraphInfo>();
|
2023-09-17 19:09:45 +02:00
|
|
|
GeometryNodesLazyFunctionBuilder builder{btree, *lf_graph_info};
|
2022-09-13 08:44:26 +02:00
|
|
|
builder.build();
|
|
|
|
|
|
|
|
|
|
lf_graph_info_ptr = std::move(lf_graph_info);
|
|
|
|
|
return lf_graph_info_ptr.get();
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 13:10:47 +02:00
|
|
|
destruct_ptr<lf::LocalUserData> GeoNodesLFUserData::get_local(LinearAllocator<> &allocator)
|
|
|
|
|
{
|
|
|
|
|
return allocator.construct<GeoNodesLFLocalUserData>(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 14:35:20 +02:00
|
|
|
void GeoNodesLFLocalUserData::ensure_tree_logger(const GeoNodesLFUserData &user_data) const
|
2023-05-09 13:10:47 +02:00
|
|
|
{
|
2023-11-29 13:22:20 +01:00
|
|
|
if (geo_eval_log::GeoModifierLog *log = user_data.call_data->eval_log) {
|
|
|
|
|
tree_logger_.emplace(&log->get_local_tree_logger(*user_data.compute_context));
|
|
|
|
|
return;
|
2023-06-29 13:57:54 +02:00
|
|
|
}
|
2023-11-22 14:48:33 +01:00
|
|
|
this->tree_logger_.emplace(nullptr);
|
2023-05-09 13:10:47 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
std::optional<FoundNestedNodeID> find_nested_node_id(const GeoNodesLFUserData &user_data,
|
|
|
|
|
const int node_id)
|
|
|
|
|
{
|
|
|
|
|
FoundNestedNodeID found;
|
|
|
|
|
Vector<int> node_ids;
|
|
|
|
|
for (const ComputeContext *context = user_data.compute_context; context != nullptr;
|
|
|
|
|
context = context->parent())
|
|
|
|
|
{
|
2023-11-21 14:12:07 +01:00
|
|
|
if (const auto *node_context = dynamic_cast<const bke::GroupNodeComputeContext *>(context)) {
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
node_ids.append(node_context->node_id());
|
|
|
|
|
}
|
|
|
|
|
else if (dynamic_cast<const bke::RepeatZoneComputeContext *>(context) != nullptr) {
|
|
|
|
|
found.is_in_loop = true;
|
|
|
|
|
}
|
|
|
|
|
else if (dynamic_cast<const bke::SimulationZoneComputeContext *>(context) != nullptr) {
|
|
|
|
|
found.is_in_simulation = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
std::reverse(node_ids.begin(), node_ids.end());
|
|
|
|
|
node_ids.append(node_id);
|
2023-11-29 13:22:20 +01:00
|
|
|
const bNestedNodeRef *nested_node_ref =
|
|
|
|
|
user_data.call_data->root_ntree->nested_node_ref_from_node_id_path(node_ids);
|
Geometry Nodes: refactor simulation storage and how simulation nodes access it
Goals of the refactor:
* Internal support for baking individual simulation zones (not exposed in the UI yet).
* More well-defined access to simulation data in geometry nodes. Especially, it
should be more obvious where data is modified. A similar approach should also
work for the Bake node.
Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData`
and then the simulation input and output nodes would have to figure out what to do with that
data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of
simulation zones. Contrary to before, different simulation zones can now be handled
independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be
subclassed by a user of the geometry nodes API. The subclass controls what each simulation
input and output node does. This some of the logic that was part of the node before, into
the modifier.
The way we store simulation data is "transposed". Previously, we stored zone data per
frame, but now we store frame data per zone. This allows different zones to be more
independent. Consequently, the way the simulation cache is accessed changed. I kept
things simpler for now, avoiding many of the methods we had before, and directly
accessing the data more often which is often simple enough. This change also makes
it theoretically possible to store baked data for separate zones independently.
A downside of this is, that existing baked data can't be read anymore. We don't really
have compatibility guarantees for this format yet, so it's ok. Users will have to bake again.
The bake folder for the modifier now contains an extra subfolder for every zone.
Drawing the cached/baked frames in the timeline is less straight forward now. Currently,
it just draws the state of one of the zones, which usually is identical to that of all other
zones. This will change in the future though, and then the timeline drawing also needs
some new UI work.
Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
|
|
|
if (nested_node_ref == nullptr) {
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
found.id = nested_node_ref->id;
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:22:20 +01:00
|
|
|
const Object *GeoNodesCallData::self_object() const
|
|
|
|
|
{
|
|
|
|
|
if (this->modifier_data) {
|
|
|
|
|
return this->modifier_data->self_object;
|
|
|
|
|
}
|
|
|
|
|
if (this->operator_data) {
|
|
|
|
|
return this->operator_data->self_object;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 08:44:26 +02:00
|
|
|
} // namespace blender::nodes
|