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.
This commit is contained in:
@@ -87,8 +87,12 @@ class SPREADSHEET_HT_header(bpy.types.Header):
|
||||
layout.label(text="Invalid id")
|
||||
elif ctx.type == 'MODIFIER':
|
||||
layout.label(text=ctx.modifier_name, icon='MODIFIER')
|
||||
elif ctx.type == 'NODE':
|
||||
layout.label(text=ctx.node_name, icon='NODE')
|
||||
elif ctx.type == 'GROUP_NODE':
|
||||
layout.label(text=ctx.ui_name, icon='NODE')
|
||||
elif ctx.type == 'SIMULATION_ZONE':
|
||||
layout.label(text="Simulation Zone")
|
||||
elif ctx.type == 'VIEWER_NODE':
|
||||
layout.label(text=ctx.ui_name)
|
||||
|
||||
def draw_spreadsheet_viewer_path_icon(self, layout, space, icon='RIGHTARROW_THIN'):
|
||||
layout.prop(space, "display_viewer_path_collapsed", icon_only=True, emboss=False, icon=icon)
|
||||
|
||||
@@ -59,4 +59,23 @@ class NodeGroupComputeContext : public ComputeContext {
|
||||
void print_current_in_line(std::ostream &stream) const override;
|
||||
};
|
||||
|
||||
class SimulationZoneComputeContext : public ComputeContext {
|
||||
private:
|
||||
static constexpr const char *s_static_type = "SIMULATION_ZONE";
|
||||
|
||||
int32_t output_node_id_;
|
||||
|
||||
public:
|
||||
SimulationZoneComputeContext(const ComputeContext *parent, int output_node_id);
|
||||
SimulationZoneComputeContext(const ComputeContext *parent, const bNode &node);
|
||||
|
||||
int32_t output_node_id() const
|
||||
{
|
||||
return output_node_id_;
|
||||
}
|
||||
|
||||
private:
|
||||
void print_current_in_line(std::ostream &stream) const override;
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
namespace blender::bke::node_tree_zones {
|
||||
|
||||
class TreeZones;
|
||||
|
||||
struct TreeZone {
|
||||
TreeZones *owner = nullptr;
|
||||
/** Index of the zone in the array of all zones in a node tree. */
|
||||
@@ -53,8 +55,25 @@ class TreeZones {
|
||||
* in a different zone than its output sockets.
|
||||
*/
|
||||
const TreeZone *get_zone_by_socket(const bNodeSocket &socket) const;
|
||||
|
||||
/**
|
||||
* Get the deepest zone that the node is in. Note that the e.g. Simulation Input and Output nodes
|
||||
* are considered to be inside of the zone they create.
|
||||
*/
|
||||
const TreeZone *get_zone_by_node(const int32_t node_id) const;
|
||||
|
||||
/**
|
||||
* Get a sorted list of zones that the node is in. First comes the root zone and last the most
|
||||
* nested zone. For nodes that are at the root level, the returned list is empty.
|
||||
*/
|
||||
Vector<const TreeZone *> get_zone_stack_for_node(const int32_t node_id) const;
|
||||
};
|
||||
|
||||
const TreeZones *get_tree_zones(const bNodeTree &tree);
|
||||
|
||||
} // namespace blender::bke::node_tree_zones
|
||||
|
||||
inline const blender::bke::node_tree_zones::TreeZones *bNodeTree::zones() const
|
||||
{
|
||||
return blender::bke::node_tree_zones::get_tree_zones(*this);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const struct IDRemapper *
|
||||
ViewerPathElem *BKE_viewer_path_elem_new(ViewerPathElemType type);
|
||||
IDViewerPathElem *BKE_viewer_path_elem_new_id(void);
|
||||
ModifierViewerPathElem *BKE_viewer_path_elem_new_modifier(void);
|
||||
NodeViewerPathElem *BKE_viewer_path_elem_new_node(void);
|
||||
GroupNodeViewerPathElem *BKE_viewer_path_elem_new_group_node(void);
|
||||
SimulationZoneViewerPathElem *BKE_viewer_path_elem_new_simulation_zone(void);
|
||||
ViewerNodeViewerPathElem *BKE_viewer_path_elem_new_viewer_node(void);
|
||||
ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src);
|
||||
bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b);
|
||||
void BKE_viewer_path_elem_free(ViewerPathElem *elem);
|
||||
|
||||
@@ -62,4 +62,30 @@ void NodeGroupComputeContext::print_current_in_line(std::ostream &stream) const
|
||||
stream << "Node ID: " << node_id_;
|
||||
}
|
||||
|
||||
SimulationZoneComputeContext::SimulationZoneComputeContext(const ComputeContext *parent,
|
||||
const int32_t output_node_id)
|
||||
: ComputeContext(s_static_type, parent), output_node_id_(output_node_id)
|
||||
{
|
||||
/* Mix static type and node id into a single buffer so that only a single call to #mix_in is
|
||||
* necessary. */
|
||||
const int type_size = strlen(s_static_type);
|
||||
const int buffer_size = type_size + 1 + sizeof(int32_t);
|
||||
DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8);
|
||||
char *buffer = static_cast<char *>(buffer_owner.buffer());
|
||||
memcpy(buffer, s_static_type, type_size + 1);
|
||||
memcpy(buffer + type_size + 1, &output_node_id_, sizeof(int32_t));
|
||||
hash_.mix_in(buffer, buffer_size);
|
||||
}
|
||||
|
||||
SimulationZoneComputeContext::SimulationZoneComputeContext(const ComputeContext *parent,
|
||||
const bNode &node)
|
||||
: SimulationZoneComputeContext(parent, node.identifier)
|
||||
{
|
||||
}
|
||||
|
||||
void SimulationZoneComputeContext::print_current_in_line(std::ostream &stream) const
|
||||
{
|
||||
stream << "Simulation Zone ID: " << output_node_id_;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
@@ -309,22 +309,44 @@ bool TreeZone::contains_zone_recursively(const TreeZone &other_zone) const
|
||||
const TreeZone *TreeZones::get_zone_by_socket(const bNodeSocket &socket) const
|
||||
{
|
||||
const bNode &node = socket.owner_node();
|
||||
const int zone_i = this->zone_by_node_id.lookup_default(node.identifier, -1);
|
||||
const TreeZone *zone = this->get_zone_by_node(node.identifier);
|
||||
if (zone == nullptr) {
|
||||
return zone;
|
||||
}
|
||||
if (zone->input_node == &node) {
|
||||
if (socket.is_input()) {
|
||||
return zone->parent_zone;
|
||||
}
|
||||
}
|
||||
if (zone->output_node == &node) {
|
||||
if (socket.is_output()) {
|
||||
return zone->parent_zone;
|
||||
}
|
||||
}
|
||||
return zone;
|
||||
}
|
||||
|
||||
const TreeZone *TreeZones::get_zone_by_node(const int32_t node_id) const
|
||||
{
|
||||
const int zone_i = this->zone_by_node_id.lookup_default(node_id, -1);
|
||||
if (zone_i == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
const TreeZone &zone = *this->zones[zone_i];
|
||||
if (zone.input_node == &node) {
|
||||
if (socket.is_input()) {
|
||||
return zone.parent_zone;
|
||||
}
|
||||
return this->zones[zone_i].get();
|
||||
}
|
||||
|
||||
Vector<const TreeZone *> TreeZones::get_zone_stack_for_node(const int node_id) const
|
||||
{
|
||||
const TreeZone *zone = this->get_zone_by_node(node_id);
|
||||
if (zone == nullptr) {
|
||||
return {};
|
||||
}
|
||||
if (zone.output_node == &node) {
|
||||
if (socket.is_output()) {
|
||||
return zone.parent_zone;
|
||||
}
|
||||
Vector<const TreeZone *> zone_stack;
|
||||
for (; zone; zone = zone->parent_zone) {
|
||||
zone_stack.append(zone);
|
||||
}
|
||||
return &zone;
|
||||
std::reverse(zone_stack.begin(), zone_stack.end());
|
||||
return zone_stack;
|
||||
}
|
||||
|
||||
} // namespace blender::bke::node_tree_zones
|
||||
|
||||
@@ -73,13 +73,23 @@ void BKE_viewer_path_blend_write(BlendWriter *writer, const ViewerPath *viewer_p
|
||||
BLO_write_string(writer, typed_elem->modifier_name);
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
const auto *typed_elem = reinterpret_cast<NodeViewerPathElem *>(elem);
|
||||
BLO_write_struct(writer, NodeViewerPathElem, typed_elem);
|
||||
BLO_write_string(writer, typed_elem->node_name);
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
|
||||
const auto *typed_elem = reinterpret_cast<GroupNodeViewerPathElem *>(elem);
|
||||
BLO_write_struct(writer, GroupNodeViewerPathElem, typed_elem);
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
|
||||
const auto *typed_elem = reinterpret_cast<SimulationZoneViewerPathElem *>(elem);
|
||||
BLO_write_struct(writer, SimulationZoneViewerPathElem, typed_elem);
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
|
||||
const auto *typed_elem = reinterpret_cast<ViewerNodeViewerPathElem *>(elem);
|
||||
BLO_write_struct(writer, ViewerNodeViewerPathElem, typed_elem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
BLO_write_string(writer, elem->ui_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +97,11 @@ void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer
|
||||
{
|
||||
BLO_read_list(reader, &viewer_path->path);
|
||||
LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) {
|
||||
BLO_read_data_address(reader, &elem->ui_name);
|
||||
switch (ViewerPathElemType(elem->type)) {
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_ID: {
|
||||
break;
|
||||
}
|
||||
@@ -96,11 +110,6 @@ void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer
|
||||
BLO_read_data_address(reader, &typed_elem->modifier_name);
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
auto *typed_elem = reinterpret_cast<NodeViewerPathElem *>(elem);
|
||||
BLO_read_data_address(reader, &typed_elem->node_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +124,9 @@ void BKE_viewer_path_blend_read_lib(BlendLibReader *reader, ID *self_id, ViewerP
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_MODIFIER:
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -132,7 +143,9 @@ void BKE_viewer_path_foreach_id(LibraryForeachIDData *data, ViewerPath *viewer_p
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_MODIFIER:
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -149,30 +162,39 @@ void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const IDRemapper *mapping
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_MODIFIER:
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> static T *make_elem(const ViewerPathElemType type)
|
||||
{
|
||||
T *elem = MEM_cnew<T>(__func__);
|
||||
elem->base.type = type;
|
||||
return elem;
|
||||
}
|
||||
|
||||
ViewerPathElem *BKE_viewer_path_elem_new(const ViewerPathElemType type)
|
||||
{
|
||||
switch (type) {
|
||||
case VIEWER_PATH_ELEM_TYPE_ID: {
|
||||
IDViewerPathElem *elem = MEM_cnew<IDViewerPathElem>(__func__);
|
||||
elem->base.type = type;
|
||||
return &elem->base;
|
||||
return &make_elem<IDViewerPathElem>(type)->base;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_MODIFIER: {
|
||||
ModifierViewerPathElem *elem = MEM_cnew<ModifierViewerPathElem>(__func__);
|
||||
elem->base.type = type;
|
||||
return &elem->base;
|
||||
return &make_elem<ModifierViewerPathElem>(type)->base;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
NodeViewerPathElem *elem = MEM_cnew<NodeViewerPathElem>(__func__);
|
||||
elem->base.type = type;
|
||||
return &elem->base;
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
|
||||
return &make_elem<GroupNodeViewerPathElem>(type)->base;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
|
||||
return &make_elem<SimulationZoneViewerPathElem>(type)->base;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
|
||||
return &make_elem<ViewerNodeViewerPathElem>(type)->base;
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
@@ -190,15 +212,30 @@ ModifierViewerPathElem *BKE_viewer_path_elem_new_modifier()
|
||||
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_MODIFIER));
|
||||
}
|
||||
|
||||
NodeViewerPathElem *BKE_viewer_path_elem_new_node()
|
||||
GroupNodeViewerPathElem *BKE_viewer_path_elem_new_group_node()
|
||||
{
|
||||
return reinterpret_cast<NodeViewerPathElem *>(
|
||||
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_NODE));
|
||||
return reinterpret_cast<GroupNodeViewerPathElem *>(
|
||||
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_GROUP_NODE));
|
||||
}
|
||||
|
||||
SimulationZoneViewerPathElem *BKE_viewer_path_elem_new_simulation_zone()
|
||||
{
|
||||
return reinterpret_cast<SimulationZoneViewerPathElem *>(
|
||||
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE));
|
||||
}
|
||||
|
||||
ViewerNodeViewerPathElem *BKE_viewer_path_elem_new_viewer_node()
|
||||
{
|
||||
return reinterpret_cast<ViewerNodeViewerPathElem *>(
|
||||
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_VIEWER_NODE));
|
||||
}
|
||||
|
||||
ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src)
|
||||
{
|
||||
ViewerPathElem *dst = BKE_viewer_path_elem_new(ViewerPathElemType(src->type));
|
||||
if (src->ui_name) {
|
||||
dst->ui_name = BLI_strdup(src->ui_name);
|
||||
}
|
||||
switch (ViewerPathElemType(src->type)) {
|
||||
case VIEWER_PATH_ELEM_TYPE_ID: {
|
||||
const auto *old_elem = reinterpret_cast<const IDViewerPathElem *>(src);
|
||||
@@ -214,13 +251,22 @@ ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
const auto *old_elem = reinterpret_cast<const NodeViewerPathElem *>(src);
|
||||
auto *new_elem = reinterpret_cast<NodeViewerPathElem *>(dst);
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
|
||||
const auto *old_elem = reinterpret_cast<const GroupNodeViewerPathElem *>(src);
|
||||
auto *new_elem = reinterpret_cast<GroupNodeViewerPathElem *>(dst);
|
||||
new_elem->node_id = old_elem->node_id;
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
|
||||
const auto *old_elem = reinterpret_cast<const SimulationZoneViewerPathElem *>(src);
|
||||
auto *new_elem = reinterpret_cast<SimulationZoneViewerPathElem *>(dst);
|
||||
new_elem->sim_output_node_id = old_elem->sim_output_node_id;
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
|
||||
const auto *old_elem = reinterpret_cast<const ViewerNodeViewerPathElem *>(src);
|
||||
auto *new_elem = reinterpret_cast<ViewerNodeViewerPathElem *>(dst);
|
||||
new_elem->node_id = old_elem->node_id;
|
||||
if (old_elem->node_name != nullptr) {
|
||||
new_elem->node_name = BLI_strdup(old_elem->node_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -243,9 +289,19 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b
|
||||
const auto *b_elem = reinterpret_cast<const ModifierViewerPathElem *>(b);
|
||||
return StringRef(a_elem->modifier_name) == StringRef(b_elem->modifier_name);
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
const auto *a_elem = reinterpret_cast<const NodeViewerPathElem *>(a);
|
||||
const auto *b_elem = reinterpret_cast<const NodeViewerPathElem *>(b);
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
|
||||
const auto *a_elem = reinterpret_cast<const GroupNodeViewerPathElem *>(a);
|
||||
const auto *b_elem = reinterpret_cast<const GroupNodeViewerPathElem *>(b);
|
||||
return a_elem->node_id == b_elem->node_id;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
|
||||
const auto *a_elem = reinterpret_cast<const SimulationZoneViewerPathElem *>(a);
|
||||
const auto *b_elem = reinterpret_cast<const SimulationZoneViewerPathElem *>(b);
|
||||
return a_elem->sim_output_node_id == b_elem->sim_output_node_id;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
|
||||
const auto *a_elem = reinterpret_cast<const ViewerNodeViewerPathElem *>(a);
|
||||
const auto *b_elem = reinterpret_cast<const ViewerNodeViewerPathElem *>(b);
|
||||
return a_elem->node_id == b_elem->node_id;
|
||||
}
|
||||
}
|
||||
@@ -255,7 +311,10 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b
|
||||
void BKE_viewer_path_elem_free(ViewerPathElem *elem)
|
||||
{
|
||||
switch (ViewerPathElemType(elem->type)) {
|
||||
case VIEWER_PATH_ELEM_TYPE_ID: {
|
||||
case VIEWER_PATH_ELEM_TYPE_ID:
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_MODIFIER: {
|
||||
@@ -263,11 +322,9 @@ void BKE_viewer_path_elem_free(ViewerPathElem *elem)
|
||||
MEM_SAFE_FREE(typed_elem->modifier_name);
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE: {
|
||||
auto *typed_elem = reinterpret_cast<NodeViewerPathElem *>(elem);
|
||||
MEM_SAFE_FREE(typed_elem->node_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (elem->ui_name) {
|
||||
MEM_freeN(elem->ui_name);
|
||||
}
|
||||
MEM_freeN(elem);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ Object *parse_object_only(const ViewerPath &viewer_path);
|
||||
struct ViewerPathForGeometryNodesViewer {
|
||||
Object *object;
|
||||
blender::StringRefNull modifier_name;
|
||||
blender::Vector<int32_t> group_node_ids;
|
||||
/* Contains only group node and simulation zone elements. */
|
||||
blender::Vector<const ViewerPathElem *> node_path;
|
||||
int32_t viewer_node_id;
|
||||
};
|
||||
|
||||
@@ -65,7 +66,6 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed
|
||||
* Checks if the node referenced by the viewer and its entire context is still active, i.e. some
|
||||
* editor is showing it.
|
||||
*/
|
||||
bool is_active_geometry_nodes_viewer(const bContext &C,
|
||||
const ViewerPathForGeometryNodesViewer &parsed_viewer_path);
|
||||
bool is_active_geometry_nodes_viewer(const bContext &C, const ViewerPath &viewer_path);
|
||||
|
||||
} // namespace blender::ed::viewer_path
|
||||
|
||||
@@ -20,11 +20,7 @@ static void validate_viewer_paths(bContext &C, WorkSpace &workspace)
|
||||
return;
|
||||
}
|
||||
|
||||
const std::optional<blender::ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
|
||||
blender::ed::viewer_path::parse_geometry_nodes_viewer(workspace.viewer_path);
|
||||
if (parsed_path.has_value() &&
|
||||
blender::ed::viewer_path::is_active_geometry_nodes_viewer(C, *parsed_path))
|
||||
{
|
||||
if (blender::ed::viewer_path::is_active_geometry_nodes_viewer(C, workspace.viewer_path)) {
|
||||
/* The current viewer path is still valid and active. */
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ struct TreeDrawContext {
|
||||
* Geometry nodes logs various data during execution. The logged data that corresponds to the
|
||||
* currently drawn node tree can be retrieved from the log below.
|
||||
*/
|
||||
geo_log::GeoTreeLog *geo_tree_log = nullptr;
|
||||
blender::Map<const TreeZone *, geo_log::GeoTreeLog *> geo_log_by_zone;
|
||||
/**
|
||||
* True if there is an active realtime compositor using the node tree, false otherwise.
|
||||
*/
|
||||
@@ -1068,8 +1068,8 @@ static void create_inspection_string_for_geometry_socket(std::stringstream &ss,
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<std::string> create_socket_inspection_string(TreeDrawContext &tree_draw_ctx,
|
||||
const bNodeSocket &socket)
|
||||
static std::optional<std::string> create_socket_inspection_string(
|
||||
geo_log::GeoTreeLog &geo_tree_log, const bNodeSocket &socket)
|
||||
{
|
||||
using namespace blender::nodes::geo_eval_log;
|
||||
|
||||
@@ -1077,8 +1077,8 @@ static std::optional<std::string> create_socket_inspection_string(TreeDrawContex
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
tree_draw_ctx.geo_tree_log->ensure_socket_values();
|
||||
ValueLog *value_log = tree_draw_ctx.geo_tree_log->find_socket_value_log(socket);
|
||||
geo_tree_log.ensure_socket_values();
|
||||
ValueLog *value_log = geo_tree_log.find_socket_value_log(socket);
|
||||
std::stringstream ss;
|
||||
if (const geo_log::GenericValueLog *generic_value_log =
|
||||
dynamic_cast<const geo_log::GenericValueLog *>(value_log))
|
||||
@@ -1131,7 +1131,8 @@ static char *node_socket_get_tooltip(const SpaceNode *snode,
|
||||
TreeDrawContext tree_draw_ctx;
|
||||
if (snode != nullptr) {
|
||||
if (ntree.type == NTREE_GEOMETRY) {
|
||||
tree_draw_ctx.geo_tree_log = geo_log::GeoModifierLog::get_tree_log_for_node_editor(*snode);
|
||||
tree_draw_ctx.geo_log_by_zone =
|
||||
geo_log::GeoModifierLog::get_tree_log_by_zone_for_node_editor(*snode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1144,13 +1145,22 @@ static char *node_socket_get_tooltip(const SpaceNode *snode,
|
||||
}
|
||||
}
|
||||
|
||||
if (ntree.type == NTREE_GEOMETRY && tree_draw_ctx.geo_tree_log != nullptr) {
|
||||
geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * {
|
||||
const TreeZones *zones = ntree.zones();
|
||||
if (!zones) {
|
||||
return nullptr;
|
||||
}
|
||||
const TreeZone *zone = zones->get_zone_by_socket(socket);
|
||||
return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
|
||||
}();
|
||||
|
||||
if (ntree.type == NTREE_GEOMETRY && geo_tree_log != nullptr) {
|
||||
if (!output.str().empty()) {
|
||||
output << ".\n\n";
|
||||
}
|
||||
|
||||
std::optional<std::string> socket_inspection_str = create_socket_inspection_string(
|
||||
tree_draw_ctx, socket);
|
||||
*geo_tree_log, socket);
|
||||
if (socket_inspection_str.has_value()) {
|
||||
output << *socket_inspection_str;
|
||||
}
|
||||
@@ -1738,9 +1748,18 @@ static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx,
|
||||
return;
|
||||
}
|
||||
|
||||
geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * {
|
||||
const TreeZones *zones = node.owner_tree().zones();
|
||||
if (!zones) {
|
||||
return nullptr;
|
||||
}
|
||||
const TreeZone *zone = zones->get_zone_by_node(node.identifier);
|
||||
return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
|
||||
}();
|
||||
|
||||
Span<geo_log::NodeWarning> warnings;
|
||||
if (tree_draw_ctx.geo_tree_log) {
|
||||
geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.identifier);
|
||||
if (geo_tree_log) {
|
||||
geo_log::GeoNodeLog *node_log = geo_tree_log->nodes.lookup_ptr(node.identifier);
|
||||
if (node_log != nullptr) {
|
||||
warnings = node_log->warnings;
|
||||
}
|
||||
@@ -1778,7 +1797,15 @@ static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx,
|
||||
static std::optional<std::chrono::nanoseconds> node_get_execution_time(
|
||||
const TreeDrawContext &tree_draw_ctx, const bNodeTree &ntree, const bNode &node)
|
||||
{
|
||||
const geo_log::GeoTreeLog *tree_log = tree_draw_ctx.geo_tree_log;
|
||||
geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * {
|
||||
const TreeZones *zones = ntree.zones();
|
||||
if (!zones) {
|
||||
return nullptr;
|
||||
}
|
||||
const TreeZone *zone = zones->get_zone_by_node(node.identifier);
|
||||
return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
|
||||
}();
|
||||
|
||||
if (tree_log == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1936,7 +1963,15 @@ static NodeExtraInfoRow row_from_used_named_attribute(
|
||||
static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(
|
||||
TreeDrawContext &tree_draw_ctx, const bNode &node)
|
||||
{
|
||||
if (tree_draw_ctx.geo_tree_log == nullptr) {
|
||||
geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * {
|
||||
const TreeZones *zones = node.owner_tree().zones();
|
||||
if (!zones) {
|
||||
return nullptr;
|
||||
}
|
||||
const TreeZone *zone = zones->get_zone_by_node(node.identifier);
|
||||
return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
|
||||
}();
|
||||
if (geo_tree_log == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (ELEM(node.type,
|
||||
@@ -1953,8 +1988,8 @@ static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(
|
||||
}
|
||||
}
|
||||
}
|
||||
tree_draw_ctx.geo_tree_log->ensure_used_named_attributes();
|
||||
geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.identifier);
|
||||
geo_tree_log->ensure_used_named_attributes();
|
||||
geo_log::GeoNodeLog *node_log = geo_tree_log->nodes.lookup_ptr(node.identifier);
|
||||
if (node_log == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1998,7 +2033,16 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(TreeDrawContext &tree_draw_c
|
||||
}
|
||||
|
||||
if (snode.edittree->type == NTREE_GEOMETRY) {
|
||||
if (geo_log::GeoTreeLog *tree_log = tree_draw_ctx.geo_tree_log) {
|
||||
geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * {
|
||||
const TreeZones *tree_zones = node.owner_tree().zones();
|
||||
if (!tree_zones) {
|
||||
return nullptr;
|
||||
}
|
||||
const TreeZone *zone = tree_zones->get_zone_by_node(node.identifier);
|
||||
return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
|
||||
}();
|
||||
|
||||
if (tree_log) {
|
||||
tree_log->ensure_debug_messages();
|
||||
const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node.identifier);
|
||||
if (node_log != nullptr) {
|
||||
@@ -3126,7 +3170,7 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/,
|
||||
const SpaceNode &snode,
|
||||
const bNodeTree &ntree)
|
||||
{
|
||||
const TreeZones *zones = bke::node_tree_zones::get_tree_zones(ntree);
|
||||
const TreeZones *zones = ntree.zones();
|
||||
if (zones == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -3368,10 +3412,11 @@ static void draw_nodetree(const bContext &C,
|
||||
|
||||
TreeDrawContext tree_draw_ctx;
|
||||
if (ntree.type == NTREE_GEOMETRY) {
|
||||
tree_draw_ctx.geo_tree_log = geo_log::GeoModifierLog::get_tree_log_for_node_editor(*snode);
|
||||
if (tree_draw_ctx.geo_tree_log != nullptr) {
|
||||
tree_draw_ctx.geo_tree_log->ensure_node_warnings();
|
||||
tree_draw_ctx.geo_tree_log->ensure_node_run_time();
|
||||
tree_draw_ctx.geo_log_by_zone = geo_log::GeoModifierLog::get_tree_log_by_zone_for_node_editor(
|
||||
*snode);
|
||||
for (geo_log::GeoTreeLog *log : tree_draw_ctx.geo_log_by_zone.values()) {
|
||||
log->ensure_node_warnings();
|
||||
log->ensure_node_run_time();
|
||||
}
|
||||
const WorkSpace *workspace = CTX_wm_workspace(&C);
|
||||
tree_draw_ctx.active_geometry_nodes_viewer = viewer_path::find_geometry_nodes_viewer(
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_update.h"
|
||||
#include "BKE_node_tree_zones.hh"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
@@ -41,6 +42,8 @@ using blender::nodes::geo_eval_log::GeometryAttributeInfo;
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
|
||||
using namespace bke::node_tree_zones;
|
||||
|
||||
struct AttributeSearchData {
|
||||
int32_t node_id;
|
||||
char socket_identifier[MAX_NAME];
|
||||
@@ -69,23 +72,33 @@ static Vector<const GeometryAttributeInfo *> get_attribute_info_from_context(
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
GeoTreeLog *tree_log = GeoModifierLog::get_tree_log_for_node_editor(*snode);
|
||||
if (tree_log == nullptr) {
|
||||
const TreeZones *tree_zones = node_tree->zones();
|
||||
if (!tree_zones) {
|
||||
return {};
|
||||
}
|
||||
tree_log->ensure_socket_values();
|
||||
const Map<const TreeZone *, GeoTreeLog *> log_by_zone =
|
||||
GeoModifierLog::get_tree_log_by_zone_for_node_editor(*snode);
|
||||
|
||||
/* For the attribute input node, collect attribute information from all nodes in the group. */
|
||||
if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE) {
|
||||
tree_log->ensure_existing_attributes();
|
||||
Vector<const GeometryAttributeInfo *> attributes;
|
||||
for (const GeometryAttributeInfo *attribute : tree_log->existing_attributes) {
|
||||
if (bke::allow_procedural_attribute_access(attribute->name)) {
|
||||
attributes.append(attribute);
|
||||
for (GeoTreeLog *tree_log : log_by_zone.values()) {
|
||||
tree_log->ensure_socket_values();
|
||||
tree_log->ensure_existing_attributes();
|
||||
for (const GeometryAttributeInfo *attribute : tree_log->existing_attributes) {
|
||||
if (bke::allow_procedural_attribute_access(attribute->name)) {
|
||||
attributes.append(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
const TreeZone *zone = tree_zones->get_zone_by_node(node->identifier);
|
||||
GeoTreeLog *tree_log = log_by_zone.lookup_default(zone, nullptr);
|
||||
if (!tree_log) {
|
||||
return {};
|
||||
}
|
||||
tree_log->ensure_socket_values();
|
||||
GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node->identifier);
|
||||
if (node_log == nullptr) {
|
||||
return {};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_zones.hh"
|
||||
#include "BKE_workspace.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
@@ -22,6 +23,8 @@
|
||||
|
||||
namespace blender::ed::viewer_path {
|
||||
|
||||
using namespace bke::node_tree_zones;
|
||||
|
||||
static void viewer_path_for_geometry_node(const SpaceNode &snode,
|
||||
const bNode &node,
|
||||
ViewerPath &r_dst)
|
||||
@@ -55,27 +58,56 @@ static void viewer_path_for_geometry_node(const SpaceNode &snode,
|
||||
modifier = nmd;
|
||||
}
|
||||
}
|
||||
if (modifier != nullptr) {
|
||||
ModifierViewerPathElem *modifier_elem = BKE_viewer_path_elem_new_modifier();
|
||||
modifier_elem->modifier_name = BLI_strdup(modifier->modifier.name);
|
||||
BLI_addtail(&r_dst.path, modifier_elem);
|
||||
if (modifier == nullptr) {
|
||||
return;
|
||||
}
|
||||
ModifierViewerPathElem *modifier_elem = BKE_viewer_path_elem_new_modifier();
|
||||
modifier_elem->modifier_name = BLI_strdup(modifier->modifier.name);
|
||||
BLI_addtail(&r_dst.path, modifier_elem);
|
||||
|
||||
Vector<const bNodeTreePath *, 16> tree_path = snode.treepath;
|
||||
for (const int i : tree_path.index_range().drop_back(1)) {
|
||||
bNodeTree *tree = tree_path[i]->nodetree;
|
||||
/* The tree path contains the name of the node but not its ID. */
|
||||
const bNode *node = nodeFindNodebyName(tree_path[i]->nodetree, tree_path[i + 1]->node_name);
|
||||
const char *node_name = tree_path[i + 1]->node_name;
|
||||
const bNode *node = nodeFindNodebyName(tree, node_name);
|
||||
/* The name in the tree path should match a group node in the tree. */
|
||||
BLI_assert(node != nullptr);
|
||||
NodeViewerPathElem *node_elem = BKE_viewer_path_elem_new_node();
|
||||
|
||||
tree->ensure_topology_cache();
|
||||
const TreeZones *tree_zones = tree->zones();
|
||||
if (!tree_zones) {
|
||||
return;
|
||||
}
|
||||
const Vector<const TreeZone *> zone_stack = tree_zones->get_zone_stack_for_node(
|
||||
node->identifier);
|
||||
for (const TreeZone *zone : zone_stack) {
|
||||
SimulationZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_simulation_zone();
|
||||
node_elem->sim_output_node_id = zone->output_node->identifier;
|
||||
BLI_addtail(&r_dst.path, node_elem);
|
||||
}
|
||||
|
||||
GroupNodeViewerPathElem *node_elem = BKE_viewer_path_elem_new_group_node();
|
||||
node_elem->node_id = node->identifier;
|
||||
node_elem->node_name = BLI_strdup(node->name);
|
||||
node_elem->base.ui_name = BLI_strdup(node->name);
|
||||
BLI_addtail(&r_dst.path, node_elem);
|
||||
}
|
||||
|
||||
NodeViewerPathElem *viewer_node_elem = BKE_viewer_path_elem_new_node();
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const TreeZones *tree_zones = snode.edittree->zones();
|
||||
if (!tree_zones) {
|
||||
return;
|
||||
}
|
||||
const Vector<const TreeZone *> zone_stack = tree_zones->get_zone_stack_for_node(node.identifier);
|
||||
for (const TreeZone *zone : zone_stack) {
|
||||
SimulationZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_simulation_zone();
|
||||
node_elem->sim_output_node_id = zone->output_node->identifier;
|
||||
BLI_addtail(&r_dst.path, node_elem);
|
||||
}
|
||||
|
||||
ViewerNodeViewerPathElem *viewer_node_elem = BKE_viewer_path_elem_new_viewer_node();
|
||||
viewer_node_elem->node_id = node.identifier;
|
||||
viewer_node_elem->node_name = BLI_strdup(node.name);
|
||||
viewer_node_elem->base.ui_name = BLI_strdup(node.name);
|
||||
BLI_addtail(&r_dst.path, viewer_node_elem);
|
||||
}
|
||||
|
||||
@@ -185,16 +217,21 @@ std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer(
|
||||
return std::nullopt;
|
||||
}
|
||||
remaining_elems = remaining_elems.drop_front(1);
|
||||
Vector<int32_t> node_ids;
|
||||
for (const ViewerPathElem *elem : remaining_elems) {
|
||||
if (elem->type != VIEWER_PATH_ELEM_TYPE_NODE) {
|
||||
Vector<const ViewerPathElem *> node_path;
|
||||
for (const ViewerPathElem *elem : remaining_elems.drop_back(1)) {
|
||||
if (!ELEM(elem->type, VIEWER_PATH_ELEM_TYPE_GROUP_NODE, VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const int32_t node_id = reinterpret_cast<const NodeViewerPathElem *>(elem)->node_id;
|
||||
node_ids.append(node_id);
|
||||
node_path.append(elem);
|
||||
}
|
||||
const int32_t viewer_node_id = node_ids.pop_last();
|
||||
return ViewerPathForGeometryNodesViewer{root_ob, modifier_name, node_ids, viewer_node_id};
|
||||
const ViewerPathElem *last_elem = remaining_elems.last();
|
||||
if (last_elem->type != VIEWER_PATH_ELEM_TYPE_VIEWER_NODE) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const int32_t viewer_node_id =
|
||||
reinterpret_cast<const ViewerNodeViewerPathElem *>(last_elem)->node_id;
|
||||
return ViewerPathForGeometryNodesViewer{root_ob, modifier_name, node_path, viewer_node_id};
|
||||
}
|
||||
|
||||
bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed_viewer_path)
|
||||
@@ -217,81 +254,72 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed
|
||||
return false;
|
||||
}
|
||||
const bNodeTree *ngroup = modifier->node_group;
|
||||
ngroup->ensure_topology_cache();
|
||||
for (const int32_t group_node_id : parsed_viewer_path.group_node_ids) {
|
||||
const bNode *group_node = nullptr;
|
||||
for (const bNode *node : ngroup->group_nodes()) {
|
||||
if (node->identifier != group_node_id) {
|
||||
continue;
|
||||
const TreeZone *zone = nullptr;
|
||||
for (const ViewerPathElem *path_elem : parsed_viewer_path.node_path) {
|
||||
ngroup->ensure_topology_cache();
|
||||
const TreeZones *tree_zones = ngroup->zones();
|
||||
switch (path_elem->type) {
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
|
||||
const auto &typed_elem = *reinterpret_cast<const SimulationZoneViewerPathElem *>(
|
||||
path_elem);
|
||||
const TreeZone *next_zone = tree_zones->get_zone_by_node(typed_elem.sim_output_node_id);
|
||||
if (next_zone == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (next_zone->parent_zone != zone) {
|
||||
return false;
|
||||
}
|
||||
zone = next_zone;
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
|
||||
const auto &typed_elem = *reinterpret_cast<const GroupNodeViewerPathElem *>(path_elem);
|
||||
const bNode *group_node = ngroup->node_by_id(typed_elem.node_id);
|
||||
if (group_node == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const TreeZone *parent_zone = tree_zones->get_zone_by_node(typed_elem.node_id);
|
||||
if (parent_zone != zone) {
|
||||
return false;
|
||||
}
|
||||
if (group_node->id == nullptr) {
|
||||
return false;
|
||||
}
|
||||
ngroup = reinterpret_cast<const bNodeTree *>(group_node->id);
|
||||
zone = nullptr;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
group_node = node;
|
||||
break;
|
||||
}
|
||||
if (group_node == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (group_node->id == nullptr) {
|
||||
return false;
|
||||
}
|
||||
ngroup = reinterpret_cast<const bNodeTree *>(group_node->id);
|
||||
}
|
||||
const bNode *viewer_node = nullptr;
|
||||
for (const bNode *node : ngroup->nodes_by_type("GeometryNodeViewer")) {
|
||||
if (node->identifier != parsed_viewer_path.viewer_node_id) {
|
||||
continue;
|
||||
}
|
||||
viewer_node = node;
|
||||
break;
|
||||
}
|
||||
|
||||
const bNode *viewer_node = ngroup->node_by_id(parsed_viewer_path.viewer_node_id);
|
||||
if (viewer_node == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool viewer_path_matches_node_editor_path(
|
||||
const SpaceNode &snode, const ViewerPathForGeometryNodesViewer &parsed_viewer_path)
|
||||
{
|
||||
Vector<const bNodeTreePath *, 16> tree_path = snode.treepath;
|
||||
if (tree_path.size() != parsed_viewer_path.group_node_ids.size() + 1) {
|
||||
const TreeZones *tree_zones = ngroup->zones();
|
||||
if (tree_zones == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (const int i : parsed_viewer_path.group_node_ids.index_range()) {
|
||||
const bNode *node = tree_path[i]->nodetree->node_by_id(parsed_viewer_path.group_node_ids[i]);
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
if (!STREQ(node->name, tree_path[i + 1]->node_name)) {
|
||||
return false;
|
||||
}
|
||||
if (tree_zones->get_zone_by_node(viewer_node->identifier) != zone) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_active_geometry_nodes_viewer(const bContext &C,
|
||||
const ViewerPathForGeometryNodesViewer &parsed_viewer_path)
|
||||
bool is_active_geometry_nodes_viewer(const bContext &C, const ViewerPath &viewer_path)
|
||||
{
|
||||
const NodesModifierData *modifier = nullptr;
|
||||
LISTBASE_FOREACH (const ModifierData *, md, &parsed_viewer_path.object->modifiers) {
|
||||
if (md->name != parsed_viewer_path.modifier_name) {
|
||||
continue;
|
||||
}
|
||||
if (md->type != eModifierType_Nodes) {
|
||||
return false;
|
||||
}
|
||||
if ((md->mode & eModifierMode_Realtime) == 0) {
|
||||
return false;
|
||||
}
|
||||
modifier = reinterpret_cast<const NodesModifierData *>(md);
|
||||
break;
|
||||
}
|
||||
if (modifier == nullptr) {
|
||||
if (BLI_listbase_is_empty(&viewer_path.path)) {
|
||||
return false;
|
||||
}
|
||||
if (modifier->node_group == nullptr) {
|
||||
const ViewerPathElem *last_elem = static_cast<ViewerPathElem *>(viewer_path.path.last);
|
||||
if (last_elem->type != VIEWER_PATH_ELEM_TYPE_VIEWER_NODE) {
|
||||
return false;
|
||||
}
|
||||
const bool modifier_is_active = modifier->modifier.flag & eModifierFlag_Active;
|
||||
const int32_t viewer_node_id =
|
||||
reinterpret_cast<const ViewerNodeViewerPathElem *>(last_elem)->node_id;
|
||||
|
||||
const Main *bmain = CTX_data_main(&C);
|
||||
const wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
|
||||
@@ -315,30 +343,26 @@ bool is_active_geometry_nodes_viewer(const bContext &C,
|
||||
continue;
|
||||
}
|
||||
const SpaceNode &snode = *reinterpret_cast<const SpaceNode *>(sl);
|
||||
if (!modifier_is_active) {
|
||||
if (!(snode.flag & SNODE_PIN)) {
|
||||
/* Node tree has to be pinned when the modifier is not active. */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (snode.id != &parsed_viewer_path.object->id) {
|
||||
if (snode.edittree == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (snode.nodetree != modifier->node_group) {
|
||||
if (snode.edittree->type != NTREE_GEOMETRY) {
|
||||
continue;
|
||||
}
|
||||
if (!viewer_path_matches_node_editor_path(snode, parsed_viewer_path)) {
|
||||
continue;
|
||||
}
|
||||
const bNodeTree *ngroup = snode.edittree;
|
||||
ngroup->ensure_topology_cache();
|
||||
const bNode *viewer_node = ngroup->node_by_id(parsed_viewer_path.viewer_node_id);
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNode *viewer_node = snode.edittree->node_by_id(viewer_node_id);
|
||||
if (viewer_node == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (!(viewer_node->flag & NODE_DO_OUTPUT)) {
|
||||
continue;
|
||||
}
|
||||
ViewerPath tmp_viewer_path{};
|
||||
BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&tmp_viewer_path); });
|
||||
viewer_path_for_geometry_node(snode, *viewer_node, tmp_viewer_path);
|
||||
if (!BKE_viewer_path_equal(&viewer_path, &tmp_viewer_path)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ class bNodeTreeRuntime;
|
||||
class bNodeRuntime;
|
||||
class bNodeSocketRuntime;
|
||||
} // namespace blender::bke
|
||||
namespace blender::bke::node_tree_zones {
|
||||
class TreeZones;
|
||||
struct TreeZone;
|
||||
} // namespace blender::bke::node_tree_zones
|
||||
using NodeDeclarationHandle = blender::nodes::NodeDeclaration;
|
||||
using SocketDeclarationHandle = blender::nodes::SocketDeclaration;
|
||||
using bNodeTreeRuntimeHandle = blender::bke::bNodeTreeRuntime;
|
||||
@@ -677,6 +681,8 @@ typedef struct bNodeTree {
|
||||
|
||||
blender::Span<const bNodePanel *> panels() const;
|
||||
blender::MutableSpan<bNodePanel *> panels_for_write();
|
||||
/** Zones in the node tree. Currently there are only simulation zones in geometry nodes. */
|
||||
const blender::bke::node_tree_zones::TreeZones *zones() const;
|
||||
#endif
|
||||
} bNodeTree;
|
||||
|
||||
|
||||
@@ -12,13 +12,16 @@ struct ID;
|
||||
typedef enum ViewerPathElemType {
|
||||
VIEWER_PATH_ELEM_TYPE_ID = 0,
|
||||
VIEWER_PATH_ELEM_TYPE_MODIFIER = 1,
|
||||
VIEWER_PATH_ELEM_TYPE_NODE = 2,
|
||||
VIEWER_PATH_ELEM_TYPE_GROUP_NODE = 2,
|
||||
VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE = 3,
|
||||
VIEWER_PATH_ELEM_TYPE_VIEWER_NODE = 4,
|
||||
} ViewerPathElemType;
|
||||
|
||||
typedef struct ViewerPathElem {
|
||||
struct ViewerPathElem *next, *prev;
|
||||
int type;
|
||||
char _pad[4];
|
||||
char *ui_name;
|
||||
} ViewerPathElem;
|
||||
|
||||
typedef struct IDViewerPathElem {
|
||||
@@ -31,18 +34,26 @@ typedef struct ModifierViewerPathElem {
|
||||
char *modifier_name;
|
||||
} ModifierViewerPathElem;
|
||||
|
||||
typedef struct NodeViewerPathElem {
|
||||
typedef struct GroupNodeViewerPathElem {
|
||||
ViewerPathElem base;
|
||||
|
||||
int32_t node_id;
|
||||
char _pad1[4];
|
||||
} GroupNodeViewerPathElem;
|
||||
|
||||
/**
|
||||
* The name of the node with the identifier. Not used to lookup nodes, only for display
|
||||
* in the UI. Still stored here to avoid looking up the name for every redraw.
|
||||
*/
|
||||
char *node_name;
|
||||
} NodeViewerPathElem;
|
||||
typedef struct SimulationZoneViewerPathElem {
|
||||
ViewerPathElem base;
|
||||
|
||||
int32_t sim_output_node_id;
|
||||
char _pad1[4];
|
||||
} SimulationZoneViewerPathElem;
|
||||
|
||||
typedef struct ViewerNodeViewerPathElem {
|
||||
ViewerPathElem base;
|
||||
|
||||
int32_t node_id;
|
||||
char _pad1[4];
|
||||
} ViewerNodeViewerPathElem;
|
||||
|
||||
typedef struct ViewerPath {
|
||||
/** List of #ViewerPathElem. */
|
||||
|
||||
@@ -3331,8 +3331,12 @@ static StructRNA *rna_viewer_path_elem_refine(PointerRNA *ptr)
|
||||
return &RNA_IDViewerPathElem;
|
||||
case VIEWER_PATH_ELEM_TYPE_MODIFIER:
|
||||
return &RNA_ModifierViewerPathElem;
|
||||
case VIEWER_PATH_ELEM_TYPE_NODE:
|
||||
return &RNA_NodeViewerPathElem;
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
|
||||
return &RNA_GroupNodeViewerPathElem;
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
|
||||
return &RNA_SimulationZoneViewerPathElem;
|
||||
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
|
||||
return &RNA_ViewerNodeViewerPathElem;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
@@ -8049,7 +8053,9 @@ static void rna_def_spreadsheet_row_filter(BlenderRNA *brna)
|
||||
static const EnumPropertyItem viewer_path_elem_type_items[] = {
|
||||
{VIEWER_PATH_ELEM_TYPE_ID, "ID", ICON_NONE, "ID", ""},
|
||||
{VIEWER_PATH_ELEM_TYPE_MODIFIER, "MODIFIER", ICON_NONE, "Modifier", ""},
|
||||
{VIEWER_PATH_ELEM_TYPE_NODE, "NODE", ICON_NONE, "Node", ""},
|
||||
{VIEWER_PATH_ELEM_TYPE_GROUP_NODE, "GROUP_NODE", ICON_NONE, "Group Node", ""},
|
||||
{VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE, "SIMULATION_ZONE", ICON_NONE, "Simulation Zone", ""},
|
||||
{VIEWER_PATH_ELEM_TYPE_VIEWER_NODE, "VIEWER_NODE", ICON_NONE, "Viewer Node", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
@@ -8066,6 +8072,11 @@ static void rna_def_viewer_path_elem(BlenderRNA *brna)
|
||||
RNA_def_property_enum_items(prop, viewer_path_elem_type_items);
|
||||
RNA_def_property_ui_text(prop, "Type", "Type of the path element");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "ui_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "UI Name", "Name that can be displayed in the UI for this element");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
}
|
||||
|
||||
static void rna_def_id_viewer_path_elem(BlenderRNA *brna)
|
||||
@@ -8090,15 +8101,37 @@ static void rna_def_modifier_viewer_path_elem(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Modifier Name", "");
|
||||
}
|
||||
|
||||
static void rna_def_node_viewer_path_elem(BlenderRNA *brna)
|
||||
static void rna_def_group_node_viewer_path_elem(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "NodeViewerPathElem", "ViewerPathElem");
|
||||
srna = RNA_def_struct(brna, "GroupNodeViewerPathElem", "ViewerPathElem");
|
||||
|
||||
prop = RNA_def_property(srna, "node_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Node Name", "");
|
||||
prop = RNA_def_property(srna, "node_id", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Node ID", "");
|
||||
}
|
||||
|
||||
static void rna_def_simulation_zone_viewer_path_elem(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "SimulationZoneViewerPathElem", "ViewerPathElem");
|
||||
|
||||
prop = RNA_def_property(srna, "sim_output_node_id", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Simulation Output Node ID", "");
|
||||
}
|
||||
|
||||
static void rna_def_viewer_node_viewer_path_elem(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "ViewerNodeViewerPathElem", "ViewerPathElem");
|
||||
|
||||
prop = RNA_def_property(srna, "node_id", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Node ID", "");
|
||||
}
|
||||
|
||||
static void rna_def_viewer_path(BlenderRNA *brna)
|
||||
@@ -8109,7 +8142,9 @@ static void rna_def_viewer_path(BlenderRNA *brna)
|
||||
rna_def_viewer_path_elem(brna);
|
||||
rna_def_id_viewer_path_elem(brna);
|
||||
rna_def_modifier_viewer_path_elem(brna);
|
||||
rna_def_node_viewer_path_elem(brna);
|
||||
rna_def_group_node_viewer_path_elem(brna);
|
||||
rna_def_simulation_zone_viewer_path_elem(brna);
|
||||
rna_def_viewer_node_viewer_path_elem(brna);
|
||||
|
||||
srna = RNA_def_struct(brna, "ViewerPath", nullptr);
|
||||
RNA_def_struct_ui_text(srna, "Viewer Path", "Path to data that is viewed");
|
||||
|
||||
@@ -424,31 +424,14 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
||||
|
||||
namespace blender {
|
||||
|
||||
static const lf::FunctionNode *find_viewer_lf_node(const bNode &viewer_bnode)
|
||||
{
|
||||
if (const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree()))
|
||||
{
|
||||
return lf_graph_info->mapping.viewer_node_map.lookup_default(&viewer_bnode, nullptr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static const lf::FunctionNode *find_group_lf_node(const bNode &group_bnode)
|
||||
{
|
||||
if (const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
nodes::ensure_geometry_nodes_lazy_function_graph(group_bnode.owner_tree()))
|
||||
{
|
||||
return lf_graph_info->mapping.group_node_map.lookup_default(&group_bnode, nullptr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void find_side_effect_nodes_for_viewer_path(
|
||||
const ViewerPath &viewer_path,
|
||||
const NodesModifierData &nmd,
|
||||
const ModifierEvalContext &ctx,
|
||||
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
|
||||
{
|
||||
using namespace bke::node_tree_zones;
|
||||
|
||||
const std::optional<ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
|
||||
ed::viewer_path::parse_geometry_nodes_viewer(viewer_path);
|
||||
if (!parsed_path.has_value()) {
|
||||
@@ -464,45 +447,100 @@ static void find_side_effect_nodes_for_viewer_path(
|
||||
ComputeContextBuilder compute_context_builder;
|
||||
compute_context_builder.push<bke::ModifierComputeContext>(parsed_path->modifier_name);
|
||||
|
||||
/* Write side effect nodes to a new map and only if everything succeeds, move the nodes to the
|
||||
* caller. This is easier than changing r_side_effect_nodes directly and then undoing changes in
|
||||
* case of errors. */
|
||||
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> local_side_effect_nodes;
|
||||
|
||||
const bNodeTree *group = nmd.node_group;
|
||||
Stack<const bNode *> group_node_stack;
|
||||
for (const int32_t group_node_id : parsed_path->group_node_ids) {
|
||||
const bNode *found_node = group->node_by_id(group_node_id);
|
||||
if (found_node == nullptr) {
|
||||
const TreeZone *zone = nullptr;
|
||||
for (const ViewerPathElem *elem : parsed_path->node_path) {
|
||||
const TreeZones *tree_zones = group->zones();
|
||||
if (tree_zones == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (found_node->id == nullptr) {
|
||||
const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*group);
|
||||
if (lf_graph_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (found_node->is_muted()) {
|
||||
return;
|
||||
switch (elem->type) {
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
|
||||
const auto &typed_elem = *reinterpret_cast<const SimulationZoneViewerPathElem *>(elem);
|
||||
const TreeZone *next_zone = tree_zones->get_zone_by_node(typed_elem.sim_output_node_id);
|
||||
if (next_zone == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (next_zone->parent_zone != zone) {
|
||||
return;
|
||||
}
|
||||
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
|
||||
next_zone, nullptr);
|
||||
if (lf_zone_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
local_side_effect_nodes.add(compute_context_builder.hash(), lf_zone_node);
|
||||
compute_context_builder.push<bke::SimulationZoneComputeContext>(*next_zone->output_node);
|
||||
zone = next_zone;
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
|
||||
const auto &typed_elem = *reinterpret_cast<const GroupNodeViewerPathElem *>(elem);
|
||||
const bNode *node = group->node_by_id(typed_elem.node_id);
|
||||
if (node == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (node->id == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (node->is_muted()) {
|
||||
return;
|
||||
}
|
||||
if (zone != tree_zones->get_zone_by_node(node->identifier)) {
|
||||
return;
|
||||
}
|
||||
const lf::FunctionNode *lf_group_node =
|
||||
lf_graph_info->mapping.group_node_map.lookup_default(node, nullptr);
|
||||
if (lf_group_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
local_side_effect_nodes.add(compute_context_builder.hash(), lf_group_node);
|
||||
compute_context_builder.push<bke::NodeGroupComputeContext>(*node);
|
||||
group = reinterpret_cast<const bNodeTree *>(node->id);
|
||||
zone = nullptr;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
}
|
||||
group_node_stack.push(found_node);
|
||||
group = reinterpret_cast<bNodeTree *>(found_node->id);
|
||||
compute_context_builder.push<bke::NodeGroupComputeContext>(*found_node);
|
||||
}
|
||||
|
||||
const bNode *found_viewer_node = group->node_by_id(parsed_path->viewer_node_id);
|
||||
if (found_viewer_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
const lf::FunctionNode *lf_viewer_node = find_viewer_lf_node(*found_viewer_node);
|
||||
const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*group);
|
||||
if (lf_graph_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
const TreeZones *tree_zones = group->zones();
|
||||
if (tree_zones == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (tree_zones->get_zone_by_node(found_viewer_node->identifier) != zone) {
|
||||
return;
|
||||
}
|
||||
const lf::FunctionNode *lf_viewer_node = lf_graph_info->mapping.viewer_node_map.lookup_default(
|
||||
found_viewer_node, nullptr);
|
||||
if (lf_viewer_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
local_side_effect_nodes.add(compute_context_builder.hash(), lf_viewer_node);
|
||||
|
||||
/* Not only mark the viewer node as having side effects, but also all group nodes it is contained
|
||||
* in. */
|
||||
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(), lf_viewer_node);
|
||||
compute_context_builder.pop();
|
||||
while (!compute_context_builder.is_empty()) {
|
||||
const lf::FunctionNode *lf_group_node = find_group_lf_node(*group_node_stack.pop());
|
||||
if (lf_group_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(), lf_group_node);
|
||||
compute_context_builder.pop();
|
||||
/* Successfully found all compute contexts for the viewer. */
|
||||
for (const auto item : local_side_effect_nodes.items()) {
|
||||
r_side_effect_nodes.add_multiple(item.key, item.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,11 +588,11 @@ static void find_socket_log_contexts(const NodesModifierData &nmd,
|
||||
const SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
|
||||
if (sl->spacetype == SPACE_NODE) {
|
||||
const SpaceNode &snode = *reinterpret_cast<const SpaceNode *>(sl);
|
||||
if (const std::optional<ComputeContextHash> hash =
|
||||
geo_log::GeoModifierLog::get_compute_context_hash_for_node_editor(
|
||||
snode, nmd.modifier.name))
|
||||
{
|
||||
r_socket_log_contexts.add(*hash);
|
||||
const Map<const bke::node_tree_zones::TreeZone *, ComputeContextHash> hash_by_zone =
|
||||
geo_log::GeoModifierLog::get_context_hash_by_zone_for_node_editor(snode,
|
||||
nmd.modifier.name);
|
||||
for (const ComputeContextHash &hash : hash_by_zone.values()) {
|
||||
r_socket_log_contexts.add(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "BLI_compute_context.hh"
|
||||
|
||||
#include "BKE_node_tree_zones.hh"
|
||||
#include "BKE_simulation_state.hh"
|
||||
|
||||
struct Object;
|
||||
@@ -175,7 +176,7 @@ struct GeometryNodeLazyFunctionGraphMapping {
|
||||
*/
|
||||
Map<const bNode *, const lf::FunctionNode *> group_node_map;
|
||||
Map<const bNode *, const lf::FunctionNode *> viewer_node_map;
|
||||
Map<const bNode *, const lf::FunctionNode *> sim_output_node_map;
|
||||
Map<const bke::node_tree_zones::TreeZone *, const lf::FunctionNode *> zone_node_map;
|
||||
|
||||
/* Indexed by #bNodeSocket::index_in_all_outputs. */
|
||||
Array<int> lf_input_index_for_output_bsocket_usage;
|
||||
@@ -190,29 +191,9 @@ struct GeometryNodeLazyFunctionGraphMapping {
|
||||
*/
|
||||
struct GeometryNodesLazyFunctionGraphInfo {
|
||||
/**
|
||||
* Allocator used for many things contained in this struct.
|
||||
* Contains resources that need to be freed when the graph is not needed anymore.
|
||||
*/
|
||||
LinearAllocator<> allocator;
|
||||
/**
|
||||
* Many nodes are implemented as multi-functions. So this contains a mapping from nodes to their
|
||||
* corresponding multi-functions.
|
||||
*/
|
||||
std::unique_ptr<NodeMultiFunctions> node_multi_functions;
|
||||
/**
|
||||
* Many lazy-functions are build for the lazy-function graph. Since the graph does not own them,
|
||||
* we have to keep track of them separately.
|
||||
*/
|
||||
Vector<std::unique_ptr<LazyFunction>> functions;
|
||||
/**
|
||||
* Debug info that has to be destructed when the graph is not used anymore.
|
||||
*/
|
||||
Vector<std::unique_ptr<lf::DummyDebugInfo>> dummy_debug_infos_;
|
||||
/**
|
||||
* Many sockets have default values. Since those are not owned by the lazy-function graph, we
|
||||
* have to keep track of them separately. This only owns the values, the memory is owned by the
|
||||
* allocator above.
|
||||
*/
|
||||
Vector<GMutablePointer> values_to_destruct;
|
||||
ResourceScope scope;
|
||||
/**
|
||||
* The actual lazy-function graph.
|
||||
*/
|
||||
@@ -226,9 +207,6 @@ struct GeometryNodesLazyFunctionGraphInfo {
|
||||
* This can be used as a simple heuristic for the complexity of the node group.
|
||||
*/
|
||||
int num_inline_nodes_approximate = 0;
|
||||
|
||||
GeometryNodesLazyFunctionGraphInfo();
|
||||
~GeometryNodesLazyFunctionGraphInfo();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_node_tree_zones.hh"
|
||||
#include "BKE_viewer_path.h"
|
||||
|
||||
#include "FN_field.hh"
|
||||
@@ -333,9 +334,11 @@ class GeoModifierLog {
|
||||
/**
|
||||
* Utility accessor to logged data.
|
||||
*/
|
||||
static std::optional<ComputeContextHash> get_compute_context_hash_for_node_editor(
|
||||
const SpaceNode &snode, StringRefNull modifier_name);
|
||||
static GeoTreeLog *get_tree_log_for_node_editor(const SpaceNode &snode);
|
||||
static Map<const bke::node_tree_zones::TreeZone *, ComputeContextHash>
|
||||
get_context_hash_by_zone_for_node_editor(const SpaceNode &snode, StringRefNull modifier_name);
|
||||
|
||||
static Map<const bke::node_tree_zones::TreeZone *, GeoTreeLog *>
|
||||
get_tree_log_by_zone_for_node_editor(const SpaceNode &snode);
|
||||
static const ViewerNodeLog *find_viewer_node_log_for_path(const ViewerPath &viewer_path);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
/** \file
|
||||
* \ingroup nodes
|
||||
*
|
||||
* This file mainly converts a #bNodeTree into a lazy-function graph. 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.
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
@@ -34,6 +39,7 @@
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_node_tree_anonymous_attributes.hh"
|
||||
#include "BKE_node_tree_zones.hh"
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
#include "FN_field_cpp_type.hh"
|
||||
@@ -45,6 +51,9 @@
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
namespace aai = bke::anonymous_attribute_inferencing;
|
||||
using bke::node_tree_zones::TreeZone;
|
||||
using bke::node_tree_zones::TreeZones;
|
||||
using fn::ValueOrField;
|
||||
using fn::ValueOrFieldCPPType;
|
||||
|
||||
@@ -1166,13 +1175,24 @@ class LazyFunctionForAnonymousAttributeSetExtract : public lf::LazyFunction {
|
||||
LazyFunctionForAnonymousAttributeSetExtract(const ValueOrFieldCPPType &type) : type_(type)
|
||||
{
|
||||
debug_name_ = "Extract Attribute Set";
|
||||
inputs_.append_as("Field", type.self);
|
||||
inputs_.append_as("Use", CPPType::get<bool>());
|
||||
inputs_.append_as("Field", type.self, lf::ValueUsage::Maybe);
|
||||
outputs_.append_as("Attributes", CPPType::get<bke::AnonymousAttributeSet>());
|
||||
}
|
||||
|
||||
void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override
|
||||
{
|
||||
const void *value_or_field = params.try_get_input_data_ptr(0);
|
||||
const bool use = params.get_input<bool>(0);
|
||||
if (!use) {
|
||||
params.set_output<bke::AnonymousAttributeSet>(0, {});
|
||||
return;
|
||||
}
|
||||
const void *value_or_field = params.try_get_input_data_ptr_or_request(1);
|
||||
if (value_or_field == nullptr) {
|
||||
/* Wait until the field is computed. */
|
||||
return;
|
||||
}
|
||||
|
||||
bke::AnonymousAttributeSet attributes;
|
||||
if (type_.is_field(value_or_field)) {
|
||||
const GField &field = *type_.get_field_ptr(value_or_field);
|
||||
@@ -1263,8 +1283,8 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction {
|
||||
/**
|
||||
* Cache for functions small amounts to avoid to avoid building them many times.
|
||||
*/
|
||||
static const LazyFunctionForAnonymousAttributeSetJoin &get_cached(
|
||||
const int amount, Vector<std::unique_ptr<LazyFunction>> &r_functions)
|
||||
static const LazyFunctionForAnonymousAttributeSetJoin &get_cached(const int amount,
|
||||
ResourceScope &scope)
|
||||
{
|
||||
constexpr int cache_amount = 16;
|
||||
static std::array<LazyFunctionForAnonymousAttributeSetJoin, cache_amount> cached_functions =
|
||||
@@ -1273,10 +1293,7 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction {
|
||||
return cached_functions[amount];
|
||||
}
|
||||
|
||||
auto fn = std::make_unique<LazyFunctionForAnonymousAttributeSetJoin>(amount);
|
||||
const auto &fn_ref = *fn;
|
||||
r_functions.append(std::move(fn));
|
||||
return fn_ref;
|
||||
return scope.construct<LazyFunctionForAnonymousAttributeSetJoin>(amount);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1288,6 +1305,122 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction {
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
if (user_data.modifier_data->socket_log_contexts) {
|
||||
zone_user_data.log_socket_values = user_data.modifier_data->socket_log_contexts->contains(
|
||||
compute_context.hash());
|
||||
}
|
||||
GeoNodesLFLocalUserData zone_local_user_data{zone_user_data};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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. */
|
||||
IndexRange main_input_indices;
|
||||
IndexRange main_output_indices;
|
||||
IndexRange border_link_input_indices;
|
||||
|
||||
IndexRange main_input_usage_indices;
|
||||
IndexRange main_output_usage_indices;
|
||||
IndexRange border_link_input_usage_indices;
|
||||
|
||||
Map<int, int> attribute_set_input_by_field_source_index;
|
||||
Map<int, int> attribute_set_input_by_caller_propagation_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class to build a lazy-function graph based on a geometry nodes tree.
|
||||
* This is mainly a separate class because it makes it easier to have variables that can be
|
||||
@@ -1296,33 +1429,12 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction {
|
||||
struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
private:
|
||||
const bNodeTree &btree_;
|
||||
const aai::AnonymousAttributeInferencingResult &attribute_inferencing_;
|
||||
ResourceScope &scope_;
|
||||
NodeMultiFunctions &node_multi_functions_;
|
||||
GeometryNodesLazyFunctionGraphInfo *lf_graph_info_;
|
||||
lf::Graph *lf_graph_;
|
||||
GeometryNodeLazyFunctionGraphMapping *mapping_;
|
||||
MultiValueMap<const bNodeSocket *, lf::InputSocket *> input_socket_map_;
|
||||
Map<const bNodeSocket *, lf::OutputSocket *> output_socket_map_;
|
||||
Map<const bNodeSocket *, lf::Node *> multi_input_socket_nodes_;
|
||||
const bke::DataTypeConversions *conversions_;
|
||||
/**
|
||||
* Maps bsockets to boolean sockets in the graph whereby each boolean socket indicates whether
|
||||
* the bsocket is used. Sockets not contained in this map are not used.
|
||||
* This is indexed by `bNodeSocket::index_in_tree()`.
|
||||
*/
|
||||
Array<lf::OutputSocket *> socket_is_used_map_;
|
||||
/**
|
||||
* Some built-in nodes get additional boolean inputs that indicate whether certain outputs are
|
||||
* used (field output sockets that contain new anonymous attribute references).
|
||||
*/
|
||||
Vector<std::pair<const bNodeSocket *, lf::InputSocket *>> output_used_sockets_for_builtin_nodes_;
|
||||
/**
|
||||
* Maps from output geometry sockets to corresponding attribute set inputs.
|
||||
*/
|
||||
Map<const bNodeSocket *, lf::InputSocket *> attribute_set_propagation_map_;
|
||||
/**
|
||||
* Boolean inputs that tell a node if some socket (of the same or another node) is used. If this
|
||||
* socket is in a link-cycle, its input can become a constant true.
|
||||
*/
|
||||
Set<const lf::InputSocket *> socket_usage_inputs_;
|
||||
|
||||
/**
|
||||
* All group input nodes are combined into one dummy node in the lazy-function graph.
|
||||
@@ -1333,12 +1445,19 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
*/
|
||||
Map<const bNode *, lf::Node *> simulation_inputs_usage_nodes_;
|
||||
|
||||
const TreeZones *tree_zones_;
|
||||
Array<ZoneBuildInfo> zone_build_infos_;
|
||||
|
||||
friend class UsedSocketVisualizeOptions;
|
||||
|
||||
public:
|
||||
GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree,
|
||||
GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
|
||||
: btree_(btree), lf_graph_info_(&lf_graph_info)
|
||||
: 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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1346,12 +1465,18 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
{
|
||||
btree_.ensure_topology_cache();
|
||||
|
||||
lf_graph_ = &lf_graph_info_->graph;
|
||||
mapping_ = &lf_graph_info_->mapping;
|
||||
conversions_ = &bke::get_implicit_type_conversions();
|
||||
tree_zones_ = btree_.zones();
|
||||
|
||||
socket_is_used_map_.reinitialize(btree_.all_sockets().size());
|
||||
socket_is_used_map_.fill(nullptr);
|
||||
this->initialize_mapping_arrays();
|
||||
this->build_zone_functions();
|
||||
this->build_root_graph();
|
||||
}
|
||||
|
||||
private:
|
||||
void initialize_mapping_arrays()
|
||||
{
|
||||
mapping_->lf_input_index_for_output_bsocket_usage.reinitialize(
|
||||
btree_.all_output_sockets().size());
|
||||
mapping_->lf_input_index_for_output_bsocket_usage.fill(-1);
|
||||
@@ -1360,37 +1485,725 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
mapping_->lf_input_index_for_attribute_propagation_to_output.fill(-1);
|
||||
mapping_->lf_index_by_bsocket.reinitialize(btree_.all_sockets().size());
|
||||
mapping_->lf_index_by_bsocket.fill(-1);
|
||||
|
||||
this->prepare_node_multi_functions();
|
||||
this->build_group_input_node();
|
||||
if (btree_.group_output_node() == nullptr) {
|
||||
this->build_fallback_output_node();
|
||||
}
|
||||
this->handle_nodes();
|
||||
this->handle_links();
|
||||
this->add_default_inputs();
|
||||
|
||||
this->build_attribute_propagation_input_node();
|
||||
this->build_output_usage_input_node();
|
||||
this->build_input_usage_output_node();
|
||||
this->build_socket_usages();
|
||||
|
||||
this->build_attribute_propagation_sets();
|
||||
this->fix_link_cycles();
|
||||
|
||||
// this->print_graph();
|
||||
|
||||
lf_graph_->update_node_indices();
|
||||
lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size();
|
||||
}
|
||||
|
||||
private:
|
||||
void prepare_node_multi_functions()
|
||||
/**
|
||||
* Builds lazy-functions for all zones in the node tree.
|
||||
*/
|
||||
void build_zone_functions()
|
||||
{
|
||||
lf_graph_info_->node_multi_functions = std::make_unique<NodeMultiFunctions>(btree_);
|
||||
zone_build_infos_.reinitialize(tree_zones_->zones.size());
|
||||
|
||||
const Array<int> zone_build_order = this->compute_zone_build_order();
|
||||
|
||||
for (const int zone_i : zone_build_order) {
|
||||
const TreeZone &zone = *tree_zones_->zones[zone_i];
|
||||
BLI_assert(zone.output_node->type == GEO_NODE_SIMULATION_OUTPUT);
|
||||
this->build_simulation_zone_function(zone);
|
||||
}
|
||||
}
|
||||
|
||||
void build_group_input_node()
|
||||
Array<int> compute_zone_build_order()
|
||||
{
|
||||
/* Build nested zones first. */
|
||||
Array<int> zone_build_order(tree_zones_->zones.size());
|
||||
std::iota(zone_build_order.begin(), zone_build_order.end(), 0);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a lazy-function for a simulation zone.
|
||||
* Internally, the generated lazy-function is just another graph.
|
||||
*/
|
||||
void build_simulation_zone_function(const TreeZone &zone)
|
||||
{
|
||||
const int zone_i = zone.index;
|
||||
ZoneBuildInfo &zone_info = zone_build_infos_[zone_i];
|
||||
lf::Graph &lf_graph = scope_.construct<lf::Graph>();
|
||||
|
||||
lf::Node *lf_zone_input_node = nullptr;
|
||||
lf::Node *lf_main_input_usage_node = nullptr;
|
||||
if (zone.input_node != nullptr) {
|
||||
lf_zone_input_node = &this->build_simulation_zone_input_node(zone, lf_graph);
|
||||
lf_main_input_usage_node = &this->build_simulation_zone_input_usage_node(zone, lf_graph);
|
||||
}
|
||||
lf::Node &lf_border_link_input_node = this->build_zone_border_links_input_node(zone, lf_graph);
|
||||
lf::Node &lf_zone_output_node = this->build_simulation_zone_output_node(zone, lf_graph);
|
||||
lf::Node &lf_main_output_usage_node = this->build_simulation_zone_output_usage_node(zone,
|
||||
lf_graph);
|
||||
lf::Node &lf_border_link_usage_node = this->build_border_link_input_usage_node(zone, lf_graph);
|
||||
|
||||
lf::Node &lf_simulation_usage_node = [&]() -> lf::Node & {
|
||||
auto &lazy_function = scope_.construct<LazyFunctionForSimulationInputsUsage>();
|
||||
lf::Node &lf_node = lf_graph.add_function(lazy_function);
|
||||
|
||||
if (lf_main_input_usage_node) {
|
||||
for (const int i : lf_main_input_usage_node->inputs().index_range()) {
|
||||
lf_graph.add_link(lf_node.output(0), lf_main_input_usage_node->input(i));
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
this->link_border_link_inputs_and_usages(
|
||||
zone, lf_border_link_input_node, lf_border_link_usage_node, graph_params);
|
||||
|
||||
if (lf_zone_input_node != nullptr) {
|
||||
for (const int i : lf_zone_input_node->outputs().index_range()) {
|
||||
lf_graph.add_link(lf_zone_input_node->output(i), lf_simulation_input->input(i));
|
||||
}
|
||||
}
|
||||
for (const int i : lf_zone_output_node.inputs().index_range()) {
|
||||
lf_graph.add_link(lf_simulation_output.output(i), lf_zone_output_node.input(i));
|
||||
}
|
||||
|
||||
this->add_default_inputs(graph_params);
|
||||
|
||||
Vector<const lf::OutputSocket *, 16> lf_zone_inputs;
|
||||
if (lf_zone_input_node) {
|
||||
lf_zone_inputs.extend(lf_zone_input_node->outputs());
|
||||
zone_info.main_input_indices = lf_zone_inputs.index_range();
|
||||
}
|
||||
lf_zone_inputs.extend(lf_border_link_input_node.outputs());
|
||||
zone_info.border_link_input_indices = lf_zone_inputs.index_range().take_back(
|
||||
lf_border_link_input_node.outputs().size());
|
||||
|
||||
lf_zone_inputs.extend(lf_main_output_usage_node.outputs());
|
||||
zone_info.main_output_usage_indices = lf_zone_inputs.index_range().take_back(
|
||||
lf_main_output_usage_node.outputs().size());
|
||||
|
||||
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,
|
||||
zone_info,
|
||||
lf_attribute_set_by_field_source_index,
|
||||
lf_attribute_set_by_caller_propagation_index,
|
||||
lf_zone_inputs);
|
||||
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);
|
||||
|
||||
Vector<const lf::InputSocket *, 16> lf_zone_outputs;
|
||||
lf_zone_outputs.extend(lf_zone_output_node.inputs());
|
||||
zone_info.main_output_indices = lf_zone_outputs.index_range();
|
||||
|
||||
if (lf_main_input_usage_node) {
|
||||
lf_zone_outputs.extend(lf_main_input_usage_node->inputs());
|
||||
zone_info.main_input_usage_indices = lf_zone_outputs.index_range().take_back(
|
||||
lf_main_input_usage_node->inputs().size());
|
||||
}
|
||||
|
||||
lf_zone_outputs.extend(lf_border_link_usage_node.inputs());
|
||||
zone_info.border_link_input_usage_indices = lf_zone_outputs.index_range().take_back(
|
||||
lf_border_link_usage_node.inputs().size());
|
||||
|
||||
lf_graph.update_node_indices();
|
||||
|
||||
auto &logger = scope_.construct<GeometryNodesLazyFunctionLogger>(*lf_graph_info_);
|
||||
auto &side_effect_provider = scope_.construct<GeometryNodesLazyFunctionSideEffectProvider>();
|
||||
|
||||
const auto &lf_graph_fn = scope_.construct<lf::GraphExecutor>(
|
||||
lf_graph, lf_zone_inputs, lf_zone_outputs, &logger, &side_effect_provider);
|
||||
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";
|
||||
}
|
||||
|
||||
lf::DummyNode &build_simulation_zone_input_node(const TreeZone &zone, lf::Graph &lf_graph)
|
||||
{
|
||||
Vector<const CPPType *, 16> zone_input_types;
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Zone Input";
|
||||
for (const bNodeSocket *socket : zone.input_node->input_sockets().drop_back(1)) {
|
||||
zone_input_types.append(socket->typeinfo->geometry_nodes_cpp_type);
|
||||
debug_info.output_names.append(socket->identifier);
|
||||
}
|
||||
lf::DummyNode &node = lf_graph.add_dummy({}, zone_input_types, &debug_info);
|
||||
return node;
|
||||
}
|
||||
|
||||
lf::DummyNode &build_simulation_zone_output_node(const TreeZone &zone, lf::Graph &lf_graph)
|
||||
{
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Zone Output";
|
||||
Vector<const CPPType *, 16> zone_output_types;
|
||||
for (const bNodeSocket *socket : zone.output_node->output_sockets().drop_back(1)) {
|
||||
zone_output_types.append(socket->typeinfo->geometry_nodes_cpp_type);
|
||||
debug_info.input_names.append(socket->identifier);
|
||||
}
|
||||
lf::DummyNode &node = lf_graph.add_dummy(zone_output_types, {}, &debug_info);
|
||||
return node;
|
||||
}
|
||||
|
||||
lf::DummyNode &build_simulation_zone_input_usage_node(const TreeZone &zone, lf::Graph &lf_graph)
|
||||
{
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Input Usages";
|
||||
Vector<const CPPType *, 16> types;
|
||||
types.append_n_times(&CPPType::get<bool>(),
|
||||
zone.input_node->input_sockets().drop_back(1).size());
|
||||
debug_info.input_names.append_n_times("Usage", types.size());
|
||||
lf::DummyNode &node = lf_graph.add_dummy(types, {}, &debug_info);
|
||||
return node;
|
||||
}
|
||||
|
||||
lf::DummyNode &build_simulation_zone_output_usage_node(const TreeZone &zone, lf::Graph &lf_graph)
|
||||
{
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Output Usages";
|
||||
Vector<const CPPType *, 16> types;
|
||||
types.append_n_times(&CPPType::get<bool>(),
|
||||
zone.output_node->output_sockets().drop_back(1).size());
|
||||
debug_info.output_names.append_n_times("Usage", types.size());
|
||||
lf::DummyNode &node = lf_graph.add_dummy({}, types, &debug_info);
|
||||
return node;
|
||||
}
|
||||
|
||||
lf::DummyNode &build_zone_border_links_input_node(const TreeZone &zone, lf::Graph &lf_graph)
|
||||
{
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Border Links";
|
||||
Vector<const CPPType *, 16> border_link_types;
|
||||
for (const bNodeLink *border_link : zone.border_links) {
|
||||
border_link_types.append(border_link->tosock->typeinfo->geometry_nodes_cpp_type);
|
||||
debug_info.output_names.append(StringRef("Link from ") + border_link->fromsock->identifier);
|
||||
}
|
||||
lf::DummyNode &node = lf_graph.add_dummy({}, border_link_types, &debug_info);
|
||||
return node;
|
||||
}
|
||||
|
||||
lf::DummyNode &build_border_link_input_usage_node(const TreeZone &zone, lf::Graph &lf_graph)
|
||||
{
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Border Link Usages";
|
||||
Vector<const CPPType *, 16> types;
|
||||
types.append_n_times(&CPPType::get<bool>(), zone.border_links.size());
|
||||
debug_info.input_names.append_n_times("Usage", types.size());
|
||||
lf::DummyNode &node = lf_graph.add_dummy(types, {}, &debug_info);
|
||||
return node;
|
||||
}
|
||||
|
||||
void build_attribute_set_inputs_for_zone(
|
||||
BuildGraphParams &graph_params,
|
||||
ZoneBuildInfo &zone_info,
|
||||
Map<int, lf::OutputSocket *> &lf_attribute_set_by_field_source_index,
|
||||
Map<int, lf::OutputSocket *> &lf_attribute_set_by_caller_propagation_index,
|
||||
Vector<const lf::OutputSocket *, 16> &lf_zone_inputs)
|
||||
{
|
||||
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());
|
||||
}
|
||||
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);
|
||||
}
|
||||
else {
|
||||
input_by_field_source_index.add_new(field_source_index,
|
||||
input_by_field_source_index.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Attribute Sets";
|
||||
Vector<const CPPType *, 16> types;
|
||||
const int num = input_by_field_source_index.size() +
|
||||
all_required_caller_propagation_indices.size();
|
||||
types.append_n_times(&CPPType::get<bke::AnonymousAttributeSet>(), num);
|
||||
debug_info.output_names.append_n_times("Set", num);
|
||||
lf::DummyNode &node = graph_params.lf_graph.add_dummy({}, types, &debug_info);
|
||||
|
||||
for (const auto item : input_by_field_source_index.items()) {
|
||||
const int field_source_index = item.key;
|
||||
const int attribute_set_index = item.value;
|
||||
lf::OutputSocket &lf_attribute_set_socket = node.output(attribute_set_index);
|
||||
lf_attribute_set_by_field_source_index.add(field_source_index, &lf_attribute_set_socket);
|
||||
|
||||
const int zone_input_index = lf_zone_inputs.append_and_get_index(&lf_attribute_set_socket);
|
||||
zone_info.attribute_set_input_by_field_source_index.add(field_source_index,
|
||||
zone_input_index);
|
||||
}
|
||||
for (const int i : all_required_caller_propagation_indices.index_range()) {
|
||||
const int caller_propagation_index = all_required_caller_propagation_indices[i];
|
||||
lf::OutputSocket &lf_attribute_set_socket = node.output(
|
||||
input_by_field_source_index.size() + i);
|
||||
lf_attribute_set_by_caller_propagation_index.add_new(caller_propagation_index,
|
||||
&lf_attribute_set_socket);
|
||||
const int zone_input_index = lf_zone_inputs.append_and_get_index(&lf_attribute_set_socket);
|
||||
zone_info.attribute_set_input_by_caller_propagation_index.add(caller_propagation_index,
|
||||
zone_input_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
lf::Node &lf_output_usage_node = this->build_output_usage_input_node(lf_graph);
|
||||
this->build_input_usage_output_node(lf_graph);
|
||||
|
||||
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)) {
|
||||
graph_params.usage_by_bsocket.add(bsocket, &lf_output_usage_node.output(bsocket->index()));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
this->build_group_input_usages(graph_params);
|
||||
this->add_default_inputs(graph_params);
|
||||
|
||||
this->build_attribute_propagation_input_node(lf_graph);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 &>(
|
||||
*mapping_->group_input_sockets[input_index]);
|
||||
lf::OutputSocket *lf_usage_socket = const_cast<lf::OutputSocket *>(
|
||||
mapping_->group_input_usage_sockets[input_index]->origin());
|
||||
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 :
|
||||
attribute_inferencing_.propagated_output_geometry_indices)
|
||||
{
|
||||
const int group_output_index =
|
||||
attribute_inferencing_.propagated_output_geometry_indices[caller_propagation_index];
|
||||
lf::OutputSocket &lf_attribute_set_socket = const_cast<lf::OutputSocket &>(
|
||||
*mapping_->attribute_set_by_geometry_output.lookup(group_output_index));
|
||||
lf_attribute_set_by_caller_propagation_index.add(caller_propagation_index,
|
||||
&lf_attribute_set_socket);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
Vector<int> indices;
|
||||
bits::foreach_1_index(all_required_field_sources, [&](const int i) { indices.append(i); });
|
||||
return indices;
|
||||
}
|
||||
|
||||
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()];
|
||||
}
|
||||
for (const int caller_propagation_index :
|
||||
lf_attribute_set_input_by_caller_propagation_index.keys())
|
||||
{
|
||||
all_required_caller_propagation_indices[caller_propagation_index].set();
|
||||
}
|
||||
|
||||
Vector<int> indices;
|
||||
bits::foreach_1_index(all_required_caller_propagation_indices,
|
||||
[&](const int i) { indices.append(i); });
|
||||
return indices;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
JoinAttributeSetsCache join_attribute_sets_cache;
|
||||
|
||||
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>(
|
||||
&field_source.data)) {
|
||||
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,
|
||||
const Span<const TreeZone *> zones,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
Vector<const bNode *> nodes_to_insert = bnodes;
|
||||
Map<const bNode *, const TreeZone *> zone_by_output;
|
||||
for (const TreeZone *zone : zones) {
|
||||
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);
|
||||
if (const TreeZone *zone = zone_by_output.lookup_default(bnode, nullptr)) {
|
||||
this->insert_child_zone_node(*zone, graph_params);
|
||||
}
|
||||
else {
|
||||
this->insert_node_in_graph(*bnode, graph_params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void link_border_link_inputs_and_usages(const TreeZone &zone,
|
||||
lf::Node &lf_border_link_input_node,
|
||||
lf::Node &lf_border_link_usage_node,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
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];
|
||||
lf::OutputSocket &lf_from = lf_border_link_input_node.output(border_link_i);
|
||||
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);
|
||||
}
|
||||
lf::InputSocket &lf_usage_output = lf_border_link_usage_node.input(border_link_i);
|
||||
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)
|
||||
{
|
||||
const ValueOrFieldCPPType &type = *ValueOrFieldCPPType::get_from_self(lf_field_socket.type());
|
||||
auto &lazy_function = scope_.construct<LazyFunctionForAnonymousAttributeSetExtract>(type);
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
void insert_child_zone_node(const TreeZone &child_zone, BuildGraphParams &graph_params)
|
||||
{
|
||||
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);
|
||||
|
||||
for (const int i : child_zone_info.main_input_indices.index_range()) {
|
||||
const bNodeSocket &bsocket = child_zone.input_node->input_socket(i);
|
||||
lf::InputSocket &lf_input_socket = child_zone_node.input(
|
||||
child_zone_info.main_input_indices[i]);
|
||||
lf::OutputSocket &lf_usage_socket = child_zone_node.output(
|
||||
child_zone_info.main_input_usage_indices[i]);
|
||||
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);
|
||||
}
|
||||
for (const int i : child_zone_info.main_output_indices.index_range()) {
|
||||
const bNodeSocket &bsocket = child_zone.output_node->output_socket(i);
|
||||
lf::OutputSocket &lf_output_socket = child_zone_node.output(
|
||||
child_zone_info.main_output_indices[i]);
|
||||
lf::InputSocket &lf_usage_input = child_zone_node.input(
|
||||
child_zone_info.main_output_usage_indices[i]);
|
||||
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,
|
||||
nullptr)) {
|
||||
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(
|
||||
child_zone_info.border_link_input_indices[child_border_link_i]);
|
||||
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(
|
||||
child_zone_info.border_link_input_usage_indices[child_border_link_i]);
|
||||
graph_params.lf_inputs_by_bsocket.add(link.tosock, &child_border_link_input);
|
||||
graph_params.usage_by_bsocket.add(link.tosock, &lf_usage);
|
||||
}
|
||||
|
||||
for (const auto item : child_zone_info.attribute_set_input_by_field_source_index.items()) {
|
||||
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);
|
||||
}
|
||||
for (const auto item : child_zone_info.attribute_set_input_by_caller_propagation_index.items())
|
||||
{
|
||||
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);
|
||||
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)
|
||||
{
|
||||
Vector<const CPPType *, 16> input_cpp_types;
|
||||
const Span<const bNodeSocket *> interface_inputs = btree_.interface_inputs();
|
||||
@@ -1399,124 +2212,108 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
}
|
||||
|
||||
/* Create a dummy node for the group inputs. */
|
||||
auto debug_info = std::make_unique<GroupInputDebugInfo>();
|
||||
group_input_lf_node_ = &lf_graph_->add_dummy({}, input_cpp_types, debug_info.get());
|
||||
auto &debug_info = scope_.construct<GroupInputDebugInfo>();
|
||||
group_input_lf_node_ = &lf_graph.add_dummy({}, input_cpp_types, &debug_info);
|
||||
|
||||
for (const int i : interface_inputs.index_range()) {
|
||||
mapping_->group_input_sockets.append(&group_input_lf_node_->output(i));
|
||||
debug_info->socket_names.append(interface_inputs[i]->name);
|
||||
debug_info.socket_names.append(interface_inputs[i]->name);
|
||||
}
|
||||
lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
void build_fallback_output_node(lf::Graph &lf_graph)
|
||||
{
|
||||
Vector<const CPPType *, 16> output_cpp_types;
|
||||
auto debug_info = std::make_unique<GroupOutputDebugInfo>();
|
||||
auto &debug_info = scope_.construct<GroupOutputDebugInfo>();
|
||||
for (const bNodeSocket *interface_output : btree_.interface_outputs()) {
|
||||
output_cpp_types.append(interface_output->typeinfo->geometry_nodes_cpp_type);
|
||||
debug_info->socket_names.append(interface_output->name);
|
||||
debug_info.socket_names.append(interface_output->name);
|
||||
}
|
||||
|
||||
lf::Node &lf_node = lf_graph_->add_dummy(output_cpp_types, {}, debug_info.get());
|
||||
lf::Node &lf_node = lf_graph.add_dummy(output_cpp_types, {}, &debug_info);
|
||||
for (lf::InputSocket *lf_socket : lf_node.inputs()) {
|
||||
const CPPType &type = lf_socket->type();
|
||||
lf_socket->set_default_value(type.default_value());
|
||||
}
|
||||
mapping_->standard_group_output_sockets = lf_node.inputs();
|
||||
|
||||
lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info));
|
||||
}
|
||||
|
||||
void handle_nodes()
|
||||
void insert_node_in_graph(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
/* Insert all nodes into the lazy function graph. */
|
||||
for (const bNode *bnode : btree_.all_nodes()) {
|
||||
const bNodeType *node_type = bnode->typeinfo;
|
||||
if (node_type == nullptr) {
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
if (bnode->is_muted()) {
|
||||
this->handle_muted_node(*bnode);
|
||||
continue;
|
||||
case NODE_REROUTE: {
|
||||
this->build_reroute_node(bnode, graph_params);
|
||||
break;
|
||||
}
|
||||
switch (node_type->type) {
|
||||
case NODE_FRAME: {
|
||||
/* Ignored. */
|
||||
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;
|
||||
}
|
||||
default: {
|
||||
if (node_type->geometry_node_execute) {
|
||||
this->build_geometry_node(bnode, graph_params);
|
||||
break;
|
||||
}
|
||||
case NODE_REROUTE: {
|
||||
this->handle_reroute_node(*bnode);
|
||||
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;
|
||||
}
|
||||
case NODE_GROUP_INPUT: {
|
||||
this->handle_group_input_node(*bnode);
|
||||
break;
|
||||
}
|
||||
case NODE_GROUP_OUTPUT: {
|
||||
this->handle_group_output_node(*bnode);
|
||||
break;
|
||||
}
|
||||
case NODE_CUSTOM_GROUP:
|
||||
case NODE_GROUP: {
|
||||
this->handle_group_node(*bnode);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_VIEWER: {
|
||||
this->handle_viewer_node(*bnode);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SIMULATION_INPUT: {
|
||||
this->handle_simulation_input_node(btree_, *bnode);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SIMULATION_OUTPUT: {
|
||||
this->handle_simulation_output_node(*bnode);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SWITCH: {
|
||||
this->handle_switch_node(*bnode);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (node_type->geometry_node_execute) {
|
||||
this->handle_geometry_node(*bnode);
|
||||
break;
|
||||
}
|
||||
const NodeMultiFunctions::Item &fn_item = lf_graph_info_->node_multi_functions->try_get(
|
||||
*bnode);
|
||||
if (fn_item.fn != nullptr) {
|
||||
this->handle_multi_function_node(*bnode, fn_item);
|
||||
break;
|
||||
}
|
||||
if (node_type == &bke::NodeTypeUndefined) {
|
||||
this->handle_undefined_node(*bnode);
|
||||
break;
|
||||
}
|
||||
/* Nodes that don't match any of the criteria above are just ignored. */
|
||||
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 handle_muted_node(const bNode &bnode)
|
||||
void build_muted_node(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
auto lazy_function = std::make_unique<LazyFunctionForMutedNode>(bnode,
|
||||
mapping_->lf_index_by_bsocket);
|
||||
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
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);
|
||||
input_socket_map_.add(bsocket, &lf_socket);
|
||||
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()) {
|
||||
@@ -1525,12 +2322,42 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
continue;
|
||||
}
|
||||
lf::OutputSocket &lf_socket = lf_node.output(lf_index);
|
||||
output_socket_map_.add_new(bsocket, &lf_socket);
|
||||
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 handle_reroute_node(const bNode &bnode)
|
||||
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);
|
||||
@@ -1539,45 +2366,50 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lazy_function = std::make_unique<LazyFunctionForRerouteNode>(*type);
|
||||
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
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);
|
||||
input_socket_map_.add(&input_bsocket, &lf_input);
|
||||
output_socket_map_.add_new(&output_bsocket, &lf_output);
|
||||
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)
|
||||
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);
|
||||
lf::OutputSocket &lf_socket = group_input_lf_node_->output(i);
|
||||
output_socket_map_.add_new(&bsocket, &lf_socket);
|
||||
graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket);
|
||||
mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_group_output_node(const bNode &bnode)
|
||||
void build_group_output_node(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
Vector<const CPPType *, 16> output_cpp_types;
|
||||
auto debug_info = std::make_unique<GroupOutputDebugInfo>();
|
||||
auto &debug_info = scope_.construct<GroupOutputDebugInfo>();
|
||||
for (const bNodeSocket *interface_input : btree_.interface_outputs()) {
|
||||
output_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type);
|
||||
debug_info->socket_names.append(interface_input->name);
|
||||
debug_info.socket_names.append(interface_input->name);
|
||||
}
|
||||
|
||||
lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy(
|
||||
output_cpp_types, {}, debug_info.get());
|
||||
lf::DummyNode &group_output_lf_node = graph_params.lf_graph.add_dummy(
|
||||
output_cpp_types, {}, &debug_info);
|
||||
|
||||
for (const int i : group_output_lf_node.inputs().index_range()) {
|
||||
const bNodeSocket &bsocket = bnode.input_socket(i);
|
||||
lf::InputSocket &lf_socket = group_output_lf_node.input(i);
|
||||
input_socket_map_.add(&bsocket, &lf_socket);
|
||||
graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket);
|
||||
mapping_->dummy_socket_map.add(&bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
||||
}
|
||||
@@ -1585,11 +2417,9 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
if (&bnode == btree_.group_output_node()) {
|
||||
mapping_->standard_group_output_sockets = group_output_lf_node.inputs();
|
||||
}
|
||||
|
||||
lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info));
|
||||
}
|
||||
|
||||
void handle_group_node(const bNode &bnode)
|
||||
void build_group_node(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id);
|
||||
if (group_btree == nullptr) {
|
||||
@@ -1601,21 +2431,21 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lazy_function = std::make_unique<LazyFunctionForGroupNode>(
|
||||
auto &lazy_function = scope_.construct<LazyFunctionForGroupNode>(
|
||||
bnode, *group_lf_graph_info, *lf_graph_info_);
|
||||
lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
||||
|
||||
for (const int i : bnode.input_sockets().index_range()) {
|
||||
const bNodeSocket &bsocket = bnode.input_socket(i);
|
||||
BLI_assert(!bsocket.is_multi_input());
|
||||
lf::InputSocket &lf_socket = lf_node.input(i);
|
||||
input_socket_map_.add(&bsocket, &lf_socket);
|
||||
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()) {
|
||||
const bNodeSocket &bsocket = bnode.output_socket(i);
|
||||
lf::OutputSocket &lf_socket = lf_node.output(i);
|
||||
output_socket_map_.add_new(&bsocket, &lf_socket);
|
||||
graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
||||
}
|
||||
mapping_->group_node_map.add(&bnode, &lf_node);
|
||||
@@ -1629,7 +2459,7 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
if (lf_input_index != -1) {
|
||||
lf::InputSocket &lf_input = lf_node.input(lf_input_index);
|
||||
lf_input.set_default_value(&static_false);
|
||||
socket_usage_inputs_.add(&lf_input);
|
||||
graph_params.socket_usage_inputs.add(&lf_input);
|
||||
}
|
||||
}
|
||||
{
|
||||
@@ -1638,17 +2468,82 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
[bsocket->index_in_all_outputs()];
|
||||
if (lf_input_index != -1) {
|
||||
lf::InputSocket &lf_input = lf_node.input(lf_input_index);
|
||||
attribute_set_propagation_map_.add(bsocket, &lf_input);
|
||||
graph_params.lf_attribute_set_input_by_output_geometry_bsocket.add(bsocket, &lf_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
|
||||
this->build_group_node_socket_usage(bnode, lf_node, graph_params);
|
||||
}
|
||||
|
||||
void handle_geometry_node(const bNode &bnode)
|
||||
void build_group_node_socket_usage(const bNode &bnode,
|
||||
lf::FunctionNode &lf_group_node,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
auto lazy_function = std::make_unique<LazyFunctionForGeometryNode>(bnode, *lf_graph_info_);
|
||||
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
const bNodeTree *bgroup = reinterpret_cast<const bNodeTree *>(bnode.id);
|
||||
if (bgroup == nullptr) {
|
||||
return;
|
||||
}
|
||||
const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info =
|
||||
ensure_geometry_nodes_lazy_function_graph(*bgroup);
|
||||
if (group_lf_graph_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto &fn = static_cast<const LazyFunctionForGroupNode &>(lf_group_node.function());
|
||||
|
||||
for (const bNodeSocket *input_bsocket : bnode.input_sockets()) {
|
||||
const int input_index = input_bsocket->index();
|
||||
const InputUsageHint &input_usage_hint =
|
||||
group_lf_graph_info->mapping.group_input_usage_hints[input_index];
|
||||
switch (input_usage_hint.type) {
|
||||
case InputUsageHintType::Never: {
|
||||
/* Nothing to do. */
|
||||
break;
|
||||
}
|
||||
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,
|
||||
&lf_group_node.output(fn.lf_output_for_input_bsocket_usage_.lookup(input_index)));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_geometry_node(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
auto &lazy_function = scope_.construct<LazyFunctionForGeometryNode>(bnode, *lf_graph_info_);
|
||||
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()];
|
||||
@@ -1658,11 +2553,11 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
lf::InputSocket &lf_socket = lf_node.input(lf_index);
|
||||
|
||||
if (bsocket->is_multi_input()) {
|
||||
auto multi_input_lazy_function = std::make_unique<LazyFunctionForMultiInput>(*bsocket);
|
||||
lf::Node &lf_multi_input_node = lf_graph_->add_function(*multi_input_lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(multi_input_lazy_function));
|
||||
lf_graph_->add_link(lf_multi_input_node.output(0), lf_socket);
|
||||
multi_input_socket_nodes_.add_new(bsocket, &lf_multi_input_node);
|
||||
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);
|
||||
for (lf::InputSocket *lf_multi_input_socket : lf_multi_input_node.inputs()) {
|
||||
mapping_->bsockets_by_lf_socket_map.add(lf_multi_input_socket, bsocket);
|
||||
const void *default_value = lf_multi_input_socket->type().default_value();
|
||||
@@ -1670,7 +2565,7 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
}
|
||||
}
|
||||
else {
|
||||
input_socket_map_.add(bsocket, &lf_socket);
|
||||
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
||||
}
|
||||
}
|
||||
@@ -1680,7 +2575,7 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
continue;
|
||||
}
|
||||
lf::OutputSocket &lf_socket = lf_node.output(lf_index);
|
||||
output_socket_map_.add_new(bsocket, &lf_socket);
|
||||
graph_params.lf_output_by_bsocket.add_new(bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
||||
}
|
||||
|
||||
@@ -1689,9 +2584,16 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
const int lf_input_index =
|
||||
mapping_->lf_input_index_for_output_bsocket_usage[bsocket->index_in_all_outputs()];
|
||||
if (lf_input_index != -1) {
|
||||
output_used_sockets_for_builtin_nodes_.append_as(bsocket,
|
||||
&lf_node.input(lf_input_index));
|
||||
socket_usage_inputs_.add_new(&lf_node.input(lf_input_index));
|
||||
lf::InputSocket &lf_input_socket = lf_node.input(lf_input_index);
|
||||
if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(bsocket,
|
||||
nullptr)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
{
|
||||
@@ -1699,20 +2601,53 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
const int lf_input_index = mapping_->lf_input_index_for_attribute_propagation_to_output
|
||||
[bsocket->index_in_all_outputs()];
|
||||
if (lf_input_index != -1) {
|
||||
attribute_set_propagation_map_.add(bsocket, &lf_node.input(lf_input_index));
|
||||
graph_params.lf_attribute_set_input_by_output_geometry_bsocket.add(
|
||||
bsocket, &lf_node.input(lf_input_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
this->build_standard_node_input_socket_usage(bnode, graph_params);
|
||||
}
|
||||
|
||||
void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item)
|
||||
void build_standard_node_input_socket_usage(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
auto lazy_function = std::make_unique<LazyFunctionForMultiFunctionNode>(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_multi_function_node(const bNode &bnode,
|
||||
const NodeMultiFunctions::Item &fn_item,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
auto &lazy_function = scope_.construct<LazyFunctionForMultiFunctionNode>(
|
||||
bnode, fn_item, mapping_->lf_index_by_bsocket);
|
||||
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
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()];
|
||||
@@ -1721,7 +2656,7 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
}
|
||||
BLI_assert(!bsocket->is_multi_input());
|
||||
lf::InputSocket &lf_socket = lf_node.input(lf_index);
|
||||
input_socket_map_.add(bsocket, &lf_socket);
|
||||
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()) {
|
||||
@@ -1730,96 +2665,112 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
continue;
|
||||
}
|
||||
lf::OutputSocket &lf_socket = lf_node.output(lf_index);
|
||||
output_socket_map_.add(bsocket, &lf_socket);
|
||||
graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
||||
}
|
||||
|
||||
this->build_standard_node_input_socket_usage(bnode, graph_params);
|
||||
}
|
||||
|
||||
void handle_viewer_node(const bNode &bnode)
|
||||
void build_viewer_node(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
auto lazy_function = std::make_unique<LazyFunctionForViewerNode>(
|
||||
auto &lazy_function = scope_.construct<LazyFunctionForViewerNode>(
|
||||
bnode, mapping_->lf_index_by_bsocket);
|
||||
lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
lf::FunctionNode &lf_viewer_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);
|
||||
input_socket_map_.add(bsocket, &lf_socket);
|
||||
lf::InputSocket &lf_socket = lf_viewer_node.input(lf_index);
|
||||
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
||||
}
|
||||
|
||||
mapping_->viewer_node_map.add(&bnode, &lf_node);
|
||||
mapping_->viewer_node_map.add(&bnode, &lf_viewer_node);
|
||||
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_simulation_input_node(const bNodeTree &node_tree, const bNode &bnode)
|
||||
lf::FunctionNode *insert_simulation_input_node(const bNodeTree &node_tree,
|
||||
const bNode &bnode,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
const NodeGeometrySimulationInput *storage = static_cast<const NodeGeometrySimulationInput *>(
|
||||
bnode.storage);
|
||||
if (node_tree.node_by_id(storage->output_node_id) == nullptr) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<LazyFunction> lazy_function = get_simulation_input_lazy_function(
|
||||
node_tree, bnode, *lf_graph_info_);
|
||||
lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
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()]);
|
||||
input_socket_map_.add(&bsocket, &lf_socket);
|
||||
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()]);
|
||||
output_socket_map_.add(&bsocket, &lf_socket);
|
||||
graph_params.lf_output_by_bsocket.add(&bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
||||
}
|
||||
return &lf_node;
|
||||
}
|
||||
|
||||
void handle_simulation_output_node(const bNode &bnode)
|
||||
lf::FunctionNode &insert_simulation_output_node(const bNode &bnode,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
std::unique_ptr<LazyFunction> lazy_function = get_simulation_output_lazy_function(
|
||||
bnode, *lf_graph_info_);
|
||||
lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
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()]);
|
||||
input_socket_map_.add(&bsocket, &lf_socket);
|
||||
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()]);
|
||||
output_socket_map_.add(&bsocket, &lf_socket);
|
||||
graph_params.lf_output_by_bsocket.add(&bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
|
||||
}
|
||||
|
||||
mapping_->sim_output_node_map.add(&bnode, &lf_node);
|
||||
return lf_node;
|
||||
}
|
||||
|
||||
void handle_switch_node(const bNode &bnode)
|
||||
void build_switch_node(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
std::unique_ptr<LazyFunction> lazy_function = get_switch_node_lazy_function(bnode);
|
||||
lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
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);
|
||||
input_socket_map_.add(bsocket, &lf_socket);
|
||||
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
||||
input_index++;
|
||||
}
|
||||
@@ -1827,19 +2778,70 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
||||
if (bsocket->is_available()) {
|
||||
lf::OutputSocket &lf_socket = lf_node.output(0);
|
||||
output_socket_map_.add(bsocket, &lf_socket);
|
||||
graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->build_switch_node_socket_usage(bnode, graph_params);
|
||||
}
|
||||
|
||||
void handle_undefined_node(const bNode &bnode)
|
||||
void build_switch_node_socket_usage(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
auto lazy_function = std::make_unique<LazyFunctionForUndefinedNode>(
|
||||
const bNodeSocket *switch_input_bsocket = nullptr;
|
||||
const bNodeSocket *false_input_bsocket = nullptr;
|
||||
const bNodeSocket *true_input_bsocket = nullptr;
|
||||
const bNodeSocket *output_bsocket = nullptr;
|
||||
for (const bNodeSocket *socket : bnode.input_sockets()) {
|
||||
if (!socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (socket->name == StringRef("Switch")) {
|
||||
switch_input_bsocket = socket;
|
||||
}
|
||||
else if (socket->name == StringRef("False")) {
|
||||
false_input_bsocket = socket;
|
||||
}
|
||||
else if (socket->name == StringRef("True")) {
|
||||
true_input_bsocket = socket;
|
||||
}
|
||||
}
|
||||
for (const bNodeSocket *socket : bnode.output_sockets()) {
|
||||
if (socket->is_available()) {
|
||||
output_bsocket = socket;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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. */
|
||||
static const LazyFunctionForSwitchSocketUsage switch_socket_usage_fn;
|
||||
lf::Node &lf_node = graph_params.lf_graph.add_function(switch_socket_usage_fn);
|
||||
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));
|
||||
}
|
||||
else {
|
||||
if (switch_input_bsocket->default_value_typed<bNodeSocketValueBoolean>()->value) {
|
||||
graph_params.usage_by_bsocket.add(true_input_bsocket, output_is_used_socket);
|
||||
}
|
||||
else {
|
||||
graph_params.usage_by_bsocket.add(false_input_bsocket, output_is_used_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_undefined_node(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
auto &lazy_function = scope_.construct<LazyFunctionForUndefinedNode>(
|
||||
bnode, mapping_->lf_index_by_bsocket);
|
||||
lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(lazy_function);
|
||||
|
||||
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
|
||||
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
|
||||
@@ -1847,32 +2849,55 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
continue;
|
||||
}
|
||||
lf::OutputSocket &lf_socket = lf_node.output(lf_index);
|
||||
output_socket_map_.add(bsocket, &lf_socket);
|
||||
graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket);
|
||||
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_links()
|
||||
{
|
||||
for (const auto item : output_socket_map_.items()) {
|
||||
this->insert_links_from_socket(*item.key, *item.value);
|
||||
}
|
||||
}
|
||||
struct TypeWithLinks {
|
||||
const CPPType *type;
|
||||
Vector<const bNodeLink *> links;
|
||||
};
|
||||
|
||||
void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket)
|
||||
void insert_links_from_socket(const bNodeSocket &from_bsocket,
|
||||
lf::OutputSocket &from_lf_socket,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
if (bke::nodeIsDanglingReroute(&btree_, &from_bsocket.owner_node())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Span<const bNodeLink *> links_from_bsocket = from_bsocket.directly_linked_links();
|
||||
|
||||
struct TypeWithLinks {
|
||||
const CPPType *type;
|
||||
Vector<const bNodeLink *> links;
|
||||
};
|
||||
|
||||
/* 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) {
|
||||
const CPPType &to_type = *type_with_links.type;
|
||||
const Span<const bNodeLink *> links = type_with_links.links;
|
||||
|
||||
lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary(
|
||||
from_lf_socket, to_type, graph_params.lf_graph);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<TypeWithLinks> group_link_targets_by_type(const bNodeSocket &from_bsocket)
|
||||
{
|
||||
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()) {
|
||||
@@ -1899,67 +2924,56 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
}
|
||||
types_with_links.append({to_type, {link}});
|
||||
}
|
||||
return types_with_links;
|
||||
}
|
||||
|
||||
for (const TypeWithLinks &type_with_links : types_with_links) {
|
||||
const CPPType &to_type = *type_with_links.type;
|
||||
const Span<const bNodeLink *> links = type_with_links.links;
|
||||
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};
|
||||
}
|
||||
|
||||
lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary(
|
||||
from_lf_socket, to_type);
|
||||
|
||||
auto make_input_link_or_set_default = [&](lf::InputSocket &to_lf_socket) {
|
||||
if (converted_from_lf_socket == nullptr) {
|
||||
const void *default_value = to_type.default_value();
|
||||
to_lf_socket.set_default_value(default_value);
|
||||
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;
|
||||
}
|
||||
else {
|
||||
lf_graph_->add_link(*converted_from_lf_socket, to_lf_socket);
|
||||
if (multi_input_link->is_muted() || !multi_input_link->fromsock->is_available() ||
|
||||
bke::nodeIsDanglingReroute(&btree_, multi_input_link->fromnode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
for (const bNodeLink *link : links) {
|
||||
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;
|
||||
}
|
||||
if (multi_input_link->is_muted() || !multi_input_link->fromsock->is_available() ||
|
||||
bke::nodeIsDanglingReroute(&btree_, multi_input_link->fromnode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
link_index++;
|
||||
}
|
||||
if (to_bsocket.owner_node().is_muted()) {
|
||||
if (link_index == 0) {
|
||||
for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) {
|
||||
make_input_link_or_set_default(*to_lf_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
lf::Node *multi_input_lf_node = multi_input_socket_nodes_.lookup_default(&to_bsocket,
|
||||
nullptr);
|
||||
if (multi_input_lf_node == nullptr) {
|
||||
continue;
|
||||
}
|
||||
make_input_link_or_set_default(multi_input_lf_node->input(link_index));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) {
|
||||
make_input_link_or_set_default(*to_lf_socket);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
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 {};
|
||||
}
|
||||
return {&multi_input_lf_node->input(link_index)};
|
||||
}
|
||||
}
|
||||
else {
|
||||
return Vector<lf::InputSocket *>(graph_params.lf_inputs_by_bsocket.lookup(&to_bsocket));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
lf::OutputSocket *insert_type_conversion_if_necessary(lf::OutputSocket &from_socket,
|
||||
const CPPType &to_type)
|
||||
const CPPType &to_type,
|
||||
lf::Graph &lf_graph)
|
||||
{
|
||||
const CPPType &from_type = from_socket.type();
|
||||
if (from_type == to_type) {
|
||||
@@ -1972,20 +2986,19 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
const MultiFunction &multi_fn = *conversions_->get_conversion_multi_function(
|
||||
mf::DataType::ForSingle(from_field_type->value),
|
||||
mf::DataType::ForSingle(to_field_type->value));
|
||||
auto fn = std::make_unique<LazyFunctionForMultiFunctionConversion>(
|
||||
auto &fn = scope_.construct<LazyFunctionForMultiFunctionConversion>(
|
||||
multi_fn, *from_field_type, *to_field_type);
|
||||
lf::Node &conversion_node = lf_graph_->add_function(*fn);
|
||||
lf_graph_info_->functions.append(std::move(fn));
|
||||
lf_graph_->add_link(from_socket, conversion_node.input(0));
|
||||
lf::Node &conversion_node = lf_graph.add_function(fn);
|
||||
lf_graph.add_link(from_socket, conversion_node.input(0));
|
||||
return &conversion_node.output(0);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void add_default_inputs()
|
||||
void add_default_inputs(BuildGraphParams &graph_params)
|
||||
{
|
||||
for (auto item : input_socket_map_.items()) {
|
||||
for (auto item : graph_params.lf_inputs_by_bsocket.items()) {
|
||||
const bNodeSocket &bsocket = *item.key;
|
||||
const Span<lf::InputSocket *> lf_sockets = item.value;
|
||||
for (lf::InputSocket *lf_socket : lf_sockets) {
|
||||
@@ -1993,28 +3006,32 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
/* Is linked already. */
|
||||
continue;
|
||||
}
|
||||
this->add_default_input(bsocket, *lf_socket);
|
||||
this->add_default_input(bsocket, *lf_socket, graph_params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_default_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket)
|
||||
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)) {
|
||||
if (this->try_add_implicit_input(input_bsocket, input_lf_socket, graph_params)) {
|
||||
return;
|
||||
}
|
||||
GMutablePointer value = get_socket_default_value(lf_graph_info_->allocator, input_bsocket);
|
||||
GMutablePointer value = get_socket_default_value(scope_.linear_allocator(), input_bsocket);
|
||||
if (value.get() == nullptr) {
|
||||
/* Not possible to add a default value. */
|
||||
return;
|
||||
}
|
||||
input_lf_socket.set_default_value(value.get());
|
||||
if (!value.type()->is_trivially_destructible()) {
|
||||
lf_graph_info_->values_to_destruct.append(value);
|
||||
scope_.add_destruct_call([value]() mutable { value.destruct(); });
|
||||
}
|
||||
}
|
||||
|
||||
bool try_add_implicit_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket)
|
||||
bool try_add_implicit_input(const bNodeSocket &input_bsocket,
|
||||
lf::InputSocket &input_lf_socket,
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
const bNode &bnode = input_bsocket.owner_node();
|
||||
const SocketDeclaration *socket_decl = input_bsocket.runtime->declaration;
|
||||
@@ -2032,18 +3049,18 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
(*implicit_input_fn)(bnode, r_value);
|
||||
};
|
||||
const CPPType &type = input_lf_socket.type();
|
||||
auto lazy_function = std::make_unique<LazyFunctionForImplicitInput>(type, std::move(init_fn));
|
||||
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
lf_graph_->add_link(lf_node.output(0), input_lf_socket);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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()
|
||||
void build_attribute_propagation_input_node(lf::Graph &lf_graph)
|
||||
{
|
||||
const aal::RelationsInNode &tree_relations =
|
||||
btree_.runtime->anonymous_attribute_inferencing->tree_relations;
|
||||
@@ -2052,150 +3069,61 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
output_indices.append_non_duplicates(relation.to_geometry_output);
|
||||
}
|
||||
Vector<const CPPType *> cpp_types;
|
||||
auto debug_info = std::make_unique<lf::SimpleDummyDebugInfo>();
|
||||
debug_info->name = "Attributes to Propagate to Output";
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Attributes to Propagate to Output";
|
||||
cpp_types.append_n_times(&CPPType::get<bke::AnonymousAttributeSet>(), output_indices.size());
|
||||
lf::Node &lf_node = lf_graph_->add_dummy({}, cpp_types, debug_info.get());
|
||||
lf::Node &lf_node = lf_graph.add_dummy({}, cpp_types, &debug_info);
|
||||
for (const int i : output_indices.index_range()) {
|
||||
const int output_index = output_indices[i];
|
||||
mapping_->attribute_set_by_geometry_output.add(output_index, &lf_node.output(i));
|
||||
debug_info->output_names.append(btree_.interface_outputs()[output_index]->name);
|
||||
debug_info.output_names.append(btree_.interface_outputs()[output_index]->name);
|
||||
}
|
||||
lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build new boolean group inputs that indicate which group outputs are used.
|
||||
*/
|
||||
void build_output_usage_input_node()
|
||||
lf::DummyNode &build_output_usage_input_node(lf::Graph &lf_graph)
|
||||
{
|
||||
const Span<const bNodeSocket *> interface_outputs = btree_.interface_outputs();
|
||||
|
||||
Vector<const CPPType *> cpp_types;
|
||||
cpp_types.append_n_times(&CPPType::get<bool>(), interface_outputs.size());
|
||||
auto debug_info = std::make_unique<lf::SimpleDummyDebugInfo>();
|
||||
debug_info->name = "Output Socket Usage";
|
||||
lf::Node &lf_node = lf_graph_->add_dummy({}, cpp_types, debug_info.get());
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Output Socket Usage";
|
||||
lf::DummyNode &lf_node = lf_graph.add_dummy({}, cpp_types, &debug_info);
|
||||
for (const int i : interface_outputs.index_range()) {
|
||||
mapping_->group_output_used_sockets.append(&lf_node.output(i));
|
||||
debug_info->output_names.append(interface_outputs[i]->name);
|
||||
debug_info.output_names.append(interface_outputs[i]->name);
|
||||
}
|
||||
lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info));
|
||||
return lf_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build new boolean group outputs that indicate which group inputs are used depending on other
|
||||
* group inputs.
|
||||
*/
|
||||
void build_input_usage_output_node()
|
||||
void build_input_usage_output_node(lf::Graph &lf_graph)
|
||||
{
|
||||
const Span<const bNodeSocket *> interface_inputs = btree_.interface_inputs();
|
||||
|
||||
Vector<const CPPType *> cpp_types;
|
||||
cpp_types.append_n_times(&CPPType::get<bool>(), interface_inputs.size());
|
||||
auto debug_info = std::make_unique<lf::SimpleDummyDebugInfo>();
|
||||
debug_info->name = "Input Socket Usage";
|
||||
lf::Node &lf_node = lf_graph_->add_dummy(cpp_types, {}, debug_info.get());
|
||||
auto &debug_info = scope_.construct<lf::SimpleDummyDebugInfo>();
|
||||
debug_info.name = "Input Socket Usage";
|
||||
lf::Node &lf_node = lf_graph.add_dummy(cpp_types, {}, &debug_info);
|
||||
for (const int i : interface_inputs.index_range()) {
|
||||
mapping_->group_input_usage_sockets.append(&lf_node.input(i));
|
||||
debug_info->input_names.append(interface_inputs[i]->name);
|
||||
debug_info.input_names.append(interface_inputs[i]->name);
|
||||
}
|
||||
lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* For every socket we want to determine if it will be used depending on the inputs of the node
|
||||
* group (just static analysis is not enough when there are e.g. Switch nodes). This function
|
||||
* populates #socket_is_used_map_ with that information.
|
||||
*/
|
||||
void build_socket_usages()
|
||||
{
|
||||
OrSocketUsagesCache or_socket_usages_cache;
|
||||
|
||||
if (const bNode *group_output_bnode = btree_.group_output_node()) {
|
||||
/* Whether a group output is used is determined by a group input that has been created
|
||||
* exactly for this purpose. */
|
||||
for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) {
|
||||
const int index = bsocket->index();
|
||||
socket_is_used_map_[bsocket->index_in_tree()] = const_cast<lf::OutputSocket *>(
|
||||
mapping_->group_output_used_sockets[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over all nodes from right to left to determine when which sockets are used. */
|
||||
for (const bNode *bnode : btree_.toposort_right_to_left()) {
|
||||
const bNodeType *node_type = bnode->typeinfo;
|
||||
if (node_type == nullptr) {
|
||||
/* Ignore. */
|
||||
continue;
|
||||
}
|
||||
|
||||
this->build_output_socket_usages(*bnode, or_socket_usages_cache);
|
||||
|
||||
if (bnode->is_muted()) {
|
||||
this->build_muted_node_usages(*bnode, or_socket_usages_cache);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (node_type->type) {
|
||||
case NODE_GROUP_OUTPUT: {
|
||||
/* Handled before this loop already. */
|
||||
break;
|
||||
}
|
||||
case NODE_GROUP_INPUT: {
|
||||
/* Handled after this loop. */
|
||||
break;
|
||||
}
|
||||
case NODE_FRAME: {
|
||||
/* Ignored. */
|
||||
break;
|
||||
}
|
||||
case NODE_REROUTE: {
|
||||
/* The input is used exactly when the output is used. */
|
||||
socket_is_used_map_[bnode->input_socket(0).index_in_tree()] =
|
||||
socket_is_used_map_[bnode->output_socket(0).index_in_tree()];
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SWITCH: {
|
||||
this->build_switch_node_socket_usage(*bnode);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_VIEWER: {
|
||||
this->build_viewer_node_socket_usage(*bnode);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SIMULATION_INPUT: {
|
||||
this->build_simulation_input_socket_usage(*bnode);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SIMULATION_OUTPUT: {
|
||||
this->build_simulation_output_socket_usage(*bnode);
|
||||
break;
|
||||
}
|
||||
case NODE_GROUP:
|
||||
case NODE_CUSTOM_GROUP: {
|
||||
this->build_group_node_socket_usage(*bnode, or_socket_usages_cache);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this->build_standard_node_input_socket_usage(*bnode, or_socket_usages_cache);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->build_group_input_usages(or_socket_usages_cache);
|
||||
this->link_output_used_sockets_for_builtin_nodes();
|
||||
}
|
||||
|
||||
using OrSocketUsagesCache = Map<Vector<lf::OutputSocket *>, lf::OutputSocket *>;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
OrSocketUsagesCache &cache)
|
||||
BuildGraphParams &graph_params)
|
||||
{
|
||||
if (usages.is_empty()) {
|
||||
return nullptr;
|
||||
@@ -2205,19 +3133,18 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
}
|
||||
|
||||
std::sort(usages.begin(), usages.end());
|
||||
return cache.lookup_or_add_cb_as(usages, [&]() {
|
||||
auto logical_or_fn = std::make_unique<LazyFunctionForLogicalOr>(usages.size());
|
||||
lf::Node &logical_or_node = lf_graph_->add_function(*logical_or_fn);
|
||||
lf_graph_info_->functions.append(std::move(logical_or_fn));
|
||||
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);
|
||||
|
||||
for (const int i : usages.index_range()) {
|
||||
lf_graph_->add_link(*usages[i], logical_or_node.input(i));
|
||||
graph_params.lf_graph.add_link(*usages[i], logical_or_node.input(i));
|
||||
}
|
||||
return &logical_or_node.output(0);
|
||||
});
|
||||
}
|
||||
|
||||
void build_output_socket_usages(const bNode &bnode, OrSocketUsagesCache &or_socket_usages_cache)
|
||||
void build_output_socket_usages(const bNode &bnode, BuildGraphParams &graph_params)
|
||||
{
|
||||
/* Output sockets are used when any of their linked inputs are used. */
|
||||
for (const bNodeSocket *socket : bnode.output_sockets()) {
|
||||
@@ -2231,250 +3158,32 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
continue;
|
||||
}
|
||||
const bNodeSocket &target_socket = *link->tosock;
|
||||
if (lf::OutputSocket *is_used_socket = socket_is_used_map_[target_socket.index_in_tree()])
|
||||
if (lf::OutputSocket *is_used_socket = graph_params.usage_by_bsocket.lookup_default(
|
||||
&target_socket, nullptr))
|
||||
{
|
||||
target_usages.append_non_duplicates(is_used_socket);
|
||||
}
|
||||
}
|
||||
/* Combine target socket usages into the usage of the current socket. */
|
||||
socket_is_used_map_[socket->index_in_tree()] = this->or_socket_usages(
|
||||
target_usages, or_socket_usages_cache);
|
||||
graph_params.usage_by_bsocket.add(socket,
|
||||
this->or_socket_usages(target_usages, 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, OrSocketUsagesCache &or_socket_usages_cache)
|
||||
{
|
||||
/* 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 = socket_is_used_map_[output_bsocket->index_in_tree()]) {
|
||||
lf_socket_usages.append(lf_socket);
|
||||
}
|
||||
}
|
||||
socket_is_used_map_[input_bsocket.index_in_tree()] = this->or_socket_usages(
|
||||
lf_socket_usages, or_socket_usages_cache);
|
||||
}
|
||||
}
|
||||
|
||||
void build_switch_node_socket_usage(const bNode &bnode)
|
||||
{
|
||||
const bNodeSocket *switch_input_bsocket = nullptr;
|
||||
const bNodeSocket *false_input_bsocket = nullptr;
|
||||
const bNodeSocket *true_input_bsocket = nullptr;
|
||||
const bNodeSocket *output_bsocket = nullptr;
|
||||
for (const bNodeSocket *socket : bnode.input_sockets()) {
|
||||
if (!socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (socket->name == StringRef("Switch")) {
|
||||
switch_input_bsocket = socket;
|
||||
}
|
||||
else if (socket->name == StringRef("False")) {
|
||||
false_input_bsocket = socket;
|
||||
}
|
||||
else if (socket->name == StringRef("True")) {
|
||||
true_input_bsocket = socket;
|
||||
}
|
||||
}
|
||||
for (const bNodeSocket *socket : bnode.output_sockets()) {
|
||||
if (socket->is_available()) {
|
||||
output_bsocket = socket;
|
||||
break;
|
||||
}
|
||||
}
|
||||
lf::OutputSocket *output_is_used_socket = socket_is_used_map_[output_bsocket->index_in_tree()];
|
||||
if (output_is_used_socket == nullptr) {
|
||||
return;
|
||||
}
|
||||
socket_is_used_map_[switch_input_bsocket->index_in_tree()] = output_is_used_socket;
|
||||
lf::InputSocket *lf_switch_input = input_socket_map_.lookup(switch_input_bsocket)[0];
|
||||
if (lf::OutputSocket *lf_switch_origin = lf_switch_input->origin()) {
|
||||
/* 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 = lf_graph_->add_function(switch_socket_usage_fn);
|
||||
lf_graph_->add_link(*lf_switch_origin, lf_node.input(0));
|
||||
socket_is_used_map_[false_input_bsocket->index_in_tree()] = &lf_node.output(0);
|
||||
socket_is_used_map_[true_input_bsocket->index_in_tree()] = &lf_node.output(1);
|
||||
}
|
||||
else {
|
||||
if (switch_input_bsocket->default_value_typed<bNodeSocketValueBoolean>()->value) {
|
||||
socket_is_used_map_[true_input_bsocket->index_in_tree()] = output_is_used_socket;
|
||||
}
|
||||
else {
|
||||
socket_is_used_map_[false_input_bsocket->index_in_tree()] = output_is_used_socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_viewer_node_socket_usage(const bNode &bnode)
|
||||
{
|
||||
const lf::FunctionNode &lf_viewer_node = *mapping_->viewer_node_map.lookup(&bnode);
|
||||
auto lazy_function = std::make_unique<LazyFunctionForViewerInputUsage>(lf_viewer_node);
|
||||
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
|
||||
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
||||
if (bsocket->is_available()) {
|
||||
socket_is_used_map_[bsocket->index_in_tree()] = &lf_node.output(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_simulation_input_socket_usage(const bNode &bnode)
|
||||
{
|
||||
const NodeGeometrySimulationInput *storage = static_cast<const NodeGeometrySimulationInput *>(
|
||||
bnode.storage);
|
||||
const bNode *sim_output_node = btree_.node_by_id(storage->output_node_id);
|
||||
if (sim_output_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
lf::Node &lf_node = this->get_simulation_inputs_usage_node(*sim_output_node);
|
||||
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
||||
if (bsocket->is_available()) {
|
||||
socket_is_used_map_[bsocket->index_in_tree()] = &lf_node.output(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_simulation_output_socket_usage(const bNode &bnode)
|
||||
{
|
||||
lf::Node &lf_node = this->get_simulation_inputs_usage_node(bnode);
|
||||
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
|
||||
if (bsocket->is_available()) {
|
||||
socket_is_used_map_[bsocket->index_in_tree()] = &lf_node.output(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lf::Node &get_simulation_inputs_usage_node(const bNode &sim_output_bnode)
|
||||
{
|
||||
BLI_assert(sim_output_bnode.type == GEO_NODE_SIMULATION_OUTPUT);
|
||||
return *simulation_inputs_usage_nodes_.lookup_or_add_cb(&sim_output_bnode, [&]() {
|
||||
auto lazy_function = std::make_unique<LazyFunctionForSimulationInputsUsage>();
|
||||
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
return &lf_node;
|
||||
});
|
||||
}
|
||||
|
||||
void build_group_node_socket_usage(const bNode &bnode,
|
||||
OrSocketUsagesCache &or_socket_usages_cache)
|
||||
{
|
||||
const bNodeTree *bgroup = reinterpret_cast<const bNodeTree *>(bnode.id);
|
||||
if (bgroup == nullptr) {
|
||||
return;
|
||||
}
|
||||
const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info =
|
||||
ensure_geometry_nodes_lazy_function_graph(*bgroup);
|
||||
if (group_lf_graph_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
lf::FunctionNode &lf_group_node = const_cast<lf::FunctionNode &>(
|
||||
*mapping_->group_node_map.lookup(&bnode));
|
||||
const auto &fn = static_cast<const LazyFunctionForGroupNode &>(lf_group_node.function());
|
||||
|
||||
for (const bNodeSocket *input_bsocket : bnode.input_sockets()) {
|
||||
const int input_index = input_bsocket->index();
|
||||
const InputUsageHint &input_usage_hint =
|
||||
group_lf_graph_info->mapping.group_input_usage_hints[input_index];
|
||||
switch (input_usage_hint.type) {
|
||||
case InputUsageHintType::Never: {
|
||||
/* Nothing to do. */
|
||||
break;
|
||||
}
|
||||
case InputUsageHintType::DependsOnOutput: {
|
||||
Vector<lf::OutputSocket *> output_usages;
|
||||
for (const int i : input_usage_hint.output_dependencies) {
|
||||
if (lf::OutputSocket *lf_socket =
|
||||
socket_is_used_map_[bnode.output_socket(i).index_in_tree()]) {
|
||||
output_usages.append(lf_socket);
|
||||
}
|
||||
}
|
||||
socket_is_used_map_[input_bsocket->index_in_tree()] = this->or_socket_usages(
|
||||
output_usages, or_socket_usages_cache);
|
||||
break;
|
||||
}
|
||||
case InputUsageHintType::DynamicSocket: {
|
||||
socket_is_used_map_[input_bsocket->index_in_tree()] = &const_cast<lf::OutputSocket &>(
|
||||
lf_group_node.output(fn.lf_output_for_input_bsocket_usage_.lookup(input_index)));
|
||||
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 =
|
||||
socket_is_used_map_[output_bsocket->index_in_tree()]) {
|
||||
lf_graph_->add_link(*lf_output_is_used, lf_socket);
|
||||
}
|
||||
else {
|
||||
static const bool static_false = false;
|
||||
lf_socket.set_default_value(&static_false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_standard_node_input_socket_usage(const bNode &bnode,
|
||||
OrSocketUsagesCache &or_socket_usages_cache)
|
||||
{
|
||||
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 = socket_is_used_map_[output_socket->index_in_tree()]) {
|
||||
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, or_socket_usages_cache);
|
||||
if (lf_usage == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const bNodeSocket *input_socket : bnode.input_sockets()) {
|
||||
if (input_socket->is_available()) {
|
||||
socket_is_used_map_[input_socket->index_in_tree()] = lf_usage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_group_input_usages(OrSocketUsagesCache &or_socket_usages_cache)
|
||||
void build_group_input_usages(BuildGraphParams &graph_params)
|
||||
{
|
||||
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) {
|
||||
if (lf::OutputSocket *lf_socket =
|
||||
socket_is_used_map_[group_input_node->output_socket(i).index_in_tree()])
|
||||
if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default(
|
||||
&group_input_node->output_socket(i), nullptr))
|
||||
{
|
||||
target_usages.append_non_duplicates(lf_socket);
|
||||
}
|
||||
}
|
||||
|
||||
lf::OutputSocket *lf_socket = this->or_socket_usages(target_usages, or_socket_usages_cache);
|
||||
lf::OutputSocket *lf_socket = this->or_socket_usages(target_usages, graph_params);
|
||||
lf::InputSocket *lf_group_output = const_cast<lf::InputSocket *>(
|
||||
mapping_->group_input_usage_sockets[i]);
|
||||
InputUsageHint input_usage_hint;
|
||||
@@ -2484,7 +3193,7 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
input_usage_hint.type = InputUsageHintType::Never;
|
||||
}
|
||||
else {
|
||||
lf_graph_->add_link(*lf_socket, *lf_group_output);
|
||||
graph_params.lf_graph.add_link(*lf_socket, *lf_group_output);
|
||||
if (lf_socket->node().is_dummy()) {
|
||||
/* Can support slightly more complex cases where it depends on more than one output in
|
||||
* the future. */
|
||||
@@ -2500,154 +3209,21 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
void link_output_used_sockets_for_builtin_nodes()
|
||||
{
|
||||
for (const auto &[output_bsocket, lf_input] : output_used_sockets_for_builtin_nodes_) {
|
||||
if (lf::OutputSocket *lf_is_used = socket_is_used_map_[output_bsocket->index_in_tree()]) {
|
||||
lf_graph_->add_link(*lf_is_used, *lf_input);
|
||||
}
|
||||
else {
|
||||
static const bool static_false = false;
|
||||
lf_input->set_default_value(&static_false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using JoinAttibuteSetsCache = Map<Vector<lf::OutputSocket *>, lf::OutputSocket *>;
|
||||
|
||||
void build_attribute_propagation_sets()
|
||||
{
|
||||
using namespace bke::anonymous_attribute_inferencing;
|
||||
|
||||
const AnonymousAttributeInferencingResult &result =
|
||||
*btree_.runtime->anonymous_attribute_inferencing;
|
||||
|
||||
JoinAttibuteSetsCache join_attribute_sets_cache;
|
||||
|
||||
Map<lf::OutputSocket *, lf::OutputSocket *> get_attributes_node_cache;
|
||||
|
||||
auto add_get_attributes_node = [&](lf::OutputSocket &lf_field_socket) -> lf::OutputSocket & {
|
||||
return *get_attributes_node_cache.lookup_or_add_cb(&lf_field_socket, [&]() {
|
||||
const ValueOrFieldCPPType &type = *ValueOrFieldCPPType::get_from_self(
|
||||
lf_field_socket.type());
|
||||
auto lazy_function = std::make_unique<LazyFunctionForAnonymousAttributeSetExtract>(type);
|
||||
lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
|
||||
lf_graph_->add_link(lf_field_socket, lf_node.input(0));
|
||||
lf_graph_info_->functions.append(std::move(lazy_function));
|
||||
return &lf_node.output(0);
|
||||
});
|
||||
};
|
||||
|
||||
for (const MapItem<const bNodeSocket *, lf::InputSocket *> item :
|
||||
attribute_set_propagation_map_.items())
|
||||
{
|
||||
const bNodeSocket &geometry_output_bsocket = *item.key;
|
||||
lf::InputSocket &lf_attribute_set_input = *item.value;
|
||||
|
||||
const BoundedBitSpan required_fields =
|
||||
result.required_fields_by_geometry_socket[geometry_output_bsocket.index_in_tree()];
|
||||
const BoundedBitSpan required_output_propagations =
|
||||
result.propagate_to_output_by_geometry_socket[geometry_output_bsocket.index_in_tree()];
|
||||
|
||||
Vector<lf::OutputSocket *> attribute_set_sockets;
|
||||
Vector<lf::OutputSocket *> used_sockets;
|
||||
|
||||
bits::foreach_1_index(required_fields, [&](const int field_source_index) {
|
||||
const FieldSource &field_source = result.all_field_sources[field_source_index];
|
||||
lf::OutputSocket *lf_socket_usage = nullptr;
|
||||
lf::OutputSocket *lf_attribute_set_socket = nullptr;
|
||||
if (const auto *input_field = std::get_if<InputFieldSource>(&field_source.data)) {
|
||||
lf_socket_usage = const_cast<lf::InputSocket *>(
|
||||
mapping_->group_input_usage_sockets[input_field->input_index])
|
||||
->origin();
|
||||
lf::OutputSocket *lf_field_socket = const_cast<lf::OutputSocket *>(
|
||||
mapping_->group_input_sockets[input_field->input_index]);
|
||||
lf_attribute_set_socket = &add_get_attributes_node(*lf_field_socket);
|
||||
}
|
||||
else {
|
||||
const auto &socket_field = std::get<SocketFieldSource>(field_source.data);
|
||||
if (&socket_field.socket->owner_node() == &geometry_output_bsocket.owner_node()) {
|
||||
return;
|
||||
}
|
||||
lf::OutputSocket *lf_field_socket = output_socket_map_.lookup(socket_field.socket);
|
||||
lf_socket_usage = socket_is_used_map_[socket_field.socket->index_in_tree()];
|
||||
lf_attribute_set_socket = &add_get_attributes_node(*lf_field_socket);
|
||||
}
|
||||
if (lf_socket_usage) {
|
||||
attribute_set_sockets.append(lf_attribute_set_socket);
|
||||
used_sockets.append(lf_socket_usage);
|
||||
}
|
||||
});
|
||||
bits::foreach_1_index(required_output_propagations, [&](const int i) {
|
||||
const int output_geometry_index = result.propagated_output_geometry_indices[i];
|
||||
lf::OutputSocket *lf_socket_usage = const_cast<lf::OutputSocket *>(
|
||||
mapping_->group_output_used_sockets[output_geometry_index]);
|
||||
if (lf_socket_usage) {
|
||||
lf::OutputSocket *lf_attribute_set_socket = const_cast<lf::OutputSocket *>(
|
||||
mapping_->attribute_set_by_geometry_output.lookup(output_geometry_index));
|
||||
attribute_set_sockets.append(lf_attribute_set_socket);
|
||||
used_sockets.append(lf_socket_usage);
|
||||
}
|
||||
});
|
||||
if (lf::OutputSocket *joined_attribute_set = this->join_attribute_sets(
|
||||
attribute_set_sockets, used_sockets, join_attribute_sets_cache))
|
||||
{
|
||||
lf_graph_->add_link(*joined_attribute_set, lf_attribute_set_input);
|
||||
}
|
||||
else {
|
||||
static const bke::AnonymousAttributeSet empty_set;
|
||||
lf_attribute_set_input.set_default_value(&empty_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 *> attribute_set_sockets,
|
||||
const Span<lf::OutputSocket *> used_sockets,
|
||||
JoinAttibuteSetsCache &cache)
|
||||
{
|
||||
BLI_assert(attribute_set_sockets.size() == used_sockets.size());
|
||||
if (attribute_set_sockets.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vector<lf::OutputSocket *, 16> key;
|
||||
key.extend(attribute_set_sockets);
|
||||
key.extend(used_sockets);
|
||||
std::sort(key.begin(), key.end());
|
||||
return cache.lookup_or_add_cb(key, [&]() {
|
||||
const auto &lazy_function = LazyFunctionForAnonymousAttributeSetJoin::get_cached(
|
||||
attribute_set_sockets.size(), lf_graph_info_->functions);
|
||||
lf::Node &lf_node = lf_graph_->add_function(lazy_function);
|
||||
for (const int i : attribute_set_sockets.index_range()) {
|
||||
lf::InputSocket &lf_use_input = lf_node.input(lazy_function.get_use_input(i));
|
||||
socket_usage_inputs_.add(&lf_use_input);
|
||||
lf::InputSocket &lf_attributes_input = lf_node.input(
|
||||
lazy_function.get_attribute_set_input(i));
|
||||
lf_graph_->add_link(*used_sockets[i], lf_use_input);
|
||||
lf_graph_->add_link(*attribute_set_sockets[i], lf_attributes_input);
|
||||
}
|
||||
return &lf_node.output(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* Example for a cycle: There is a `Distribute Points on Faces` node and its `Normal` output is
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
void fix_link_cycles()
|
||||
void fix_link_cycles(lf::Graph &lf_graph, const Set<lf::InputSocket *> &socket_usage_inputs)
|
||||
{
|
||||
lf_graph_->update_socket_indices();
|
||||
const int sockets_num = lf_graph_->socket_num();
|
||||
lf_graph.update_socket_indices();
|
||||
const int sockets_num = lf_graph.socket_num();
|
||||
|
||||
struct SocketState {
|
||||
bool done = false;
|
||||
@@ -2657,7 +3233,7 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
Array<SocketState> socket_states(sockets_num);
|
||||
|
||||
Vector<lf::Socket *> lf_sockets_to_check;
|
||||
for (lf::Node *lf_node : lf_graph_->nodes()) {
|
||||
for (lf::Node *lf_node : lf_graph.nodes()) {
|
||||
if (lf_node->is_function()) {
|
||||
for (lf::OutputSocket *lf_socket : lf_node->outputs()) {
|
||||
if (lf_socket->targets().is_empty()) {
|
||||
@@ -2713,8 +3289,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
* computation later, but does not change correctness.
|
||||
*
|
||||
* After the cycle is broken, the cycle-detection is "rolled back" to the socket where
|
||||
* the first socket of the cycle was found. This is necessary in case another cycle goes
|
||||
* through this socket. */
|
||||
* the first socket of the cycle was found. This is necessary in case another cycle
|
||||
* goes through this socket. */
|
||||
|
||||
detected_cycle = true;
|
||||
const int index_in_socket_stack = lf_socket_stack.first_index_of(lf_origin_socket);
|
||||
@@ -2726,9 +3302,9 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
bool broke_cycle = false;
|
||||
for (lf::Socket *lf_cycle_socket : cycle) {
|
||||
if (lf_cycle_socket->is_input() &&
|
||||
socket_usage_inputs_.contains(&lf_cycle_socket->as_input())) {
|
||||
socket_usage_inputs.contains(&lf_cycle_socket->as_input())) {
|
||||
lf::InputSocket &lf_cycle_input_socket = lf_cycle_socket->as_input();
|
||||
lf_graph_->clear_origin(lf_cycle_input_socket);
|
||||
lf_graph.clear_origin(lf_cycle_input_socket);
|
||||
static const bool static_true = true;
|
||||
lf_cycle_input_socket.set_default_value(&static_true);
|
||||
broke_cycle = true;
|
||||
@@ -2765,82 +3341,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
lf_socket_stack.pop_last();
|
||||
}
|
||||
}
|
||||
|
||||
void print_graph();
|
||||
};
|
||||
|
||||
class UsedSocketVisualizeOptions : public lf::Graph::ToDotOptions {
|
||||
private:
|
||||
const GeometryNodesLazyFunctionGraphBuilder &builder_;
|
||||
Map<const lf::Socket *, std::string> socket_font_colors_;
|
||||
Map<const lf::Socket *, std::string> socket_name_suffixes_;
|
||||
|
||||
public:
|
||||
UsedSocketVisualizeOptions(const GeometryNodesLazyFunctionGraphBuilder &builder)
|
||||
: builder_(builder)
|
||||
{
|
||||
VectorSet<lf::OutputSocket *> found;
|
||||
for (const int bsocket_index : builder_.socket_is_used_map_.index_range()) {
|
||||
const bNodeSocket *bsocket = builder_.btree_.all_sockets()[bsocket_index];
|
||||
lf::OutputSocket *lf_used_socket = builder_.socket_is_used_map_[bsocket_index];
|
||||
if (lf_used_socket == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const float hue = BLI_hash_int_01(uintptr_t(lf_used_socket));
|
||||
std::stringstream ss;
|
||||
ss.precision(3);
|
||||
ss << hue << " 0.9 0.5";
|
||||
const std::string color_str = ss.str();
|
||||
const std::string suffix = " (" + std::to_string(found.index_of_or_add(lf_used_socket)) +
|
||||
")";
|
||||
socket_font_colors_.add(lf_used_socket, color_str);
|
||||
socket_name_suffixes_.add(lf_used_socket, suffix);
|
||||
|
||||
if (bsocket->is_input()) {
|
||||
for (const lf::InputSocket *lf_socket : builder_.input_socket_map_.lookup(bsocket)) {
|
||||
socket_font_colors_.add(lf_socket, color_str);
|
||||
socket_name_suffixes_.add(lf_socket, suffix);
|
||||
}
|
||||
}
|
||||
else if (lf::OutputSocket *lf_socket = builder_.output_socket_map_.lookup_default(bsocket,
|
||||
nullptr))
|
||||
{
|
||||
socket_font_colors_.add(lf_socket, color_str);
|
||||
socket_name_suffixes_.add(lf_socket, suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> socket_font_color(const lf::Socket &socket) const override
|
||||
{
|
||||
if (const std::string *color = socket_font_colors_.lookup_ptr(&socket)) {
|
||||
return *color;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string socket_name(const lf::Socket &socket) const override
|
||||
{
|
||||
return socket.name() + socket_name_suffixes_.lookup_default(&socket, "");
|
||||
}
|
||||
|
||||
void add_edge_attributes(const lf::OutputSocket & /*from*/,
|
||||
const lf::InputSocket &to,
|
||||
dot::DirectedEdge &dot_edge) const override
|
||||
{
|
||||
if (builder_.socket_usage_inputs_.contains_as(&to)) {
|
||||
// dot_edge.attributes.set("constraint", "false");
|
||||
dot_edge.attributes.set("color", "#00000055");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void GeometryNodesLazyFunctionGraphBuilder::print_graph()
|
||||
{
|
||||
UsedSocketVisualizeOptions options{*this};
|
||||
std::cout << "\n\n" << lf_graph_->to_dot(options) << "\n\n";
|
||||
}
|
||||
|
||||
const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph(
|
||||
const bNodeTree &btree)
|
||||
{
|
||||
@@ -2967,14 +3469,6 @@ Vector<const lf::FunctionNode *> GeometryNodesLazyFunctionSideEffectProvider::
|
||||
return modifier_data.side_effect_nodes->lookup(context_hash);
|
||||
}
|
||||
|
||||
GeometryNodesLazyFunctionGraphInfo::GeometryNodesLazyFunctionGraphInfo() = default;
|
||||
GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo()
|
||||
{
|
||||
for (GMutablePointer &p : this->values_to_destruct) {
|
||||
p.destruct();
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void add_thread_id_debug_message(
|
||||
const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
|
||||
const lf::FunctionNode &node,
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace blender::nodes::geo_eval_log {
|
||||
|
||||
using fn::FieldInput;
|
||||
using fn::FieldInputs;
|
||||
using namespace bke::node_tree_zones;
|
||||
|
||||
GenericValueLog::~GenericValueLog()
|
||||
{
|
||||
@@ -518,46 +519,82 @@ static std::optional<ObjectAndModifier> get_modifier_for_node_editor(const Space
|
||||
return ObjectAndModifier{object, used_modifier};
|
||||
}
|
||||
|
||||
std::optional<ComputeContextHash> GeoModifierLog::get_compute_context_hash_for_node_editor(
|
||||
const SpaceNode &snode, const StringRefNull modifier_name)
|
||||
static void find_tree_zone_hash_recursive(
|
||||
const TreeZone &zone,
|
||||
ComputeContextBuilder &compute_context_builder,
|
||||
Map<const TreeZone *, ComputeContextHash> &r_hash_by_zone)
|
||||
{
|
||||
Vector<const bNodeTreePath *> tree_path = snode.treepath;
|
||||
if (tree_path.is_empty()) {
|
||||
return std::nullopt;
|
||||
compute_context_builder.push<bke::SimulationZoneComputeContext>(*zone.output_node);
|
||||
r_hash_by_zone.add_new(&zone, compute_context_builder.hash());
|
||||
for (const TreeZone *child_zone : zone.child_zones) {
|
||||
find_tree_zone_hash_recursive(*child_zone, compute_context_builder, r_hash_by_zone);
|
||||
}
|
||||
ComputeContextBuilder compute_context_builder;
|
||||
compute_context_builder.push<bke::ModifierComputeContext>(modifier_name);
|
||||
for (const int i : tree_path.index_range().drop_back(1)) {
|
||||
/* The tree path contains the name of the node but not its ID. */
|
||||
const bNode *node = nodeFindNodebyName(tree_path[i]->nodetree, tree_path[i + 1]->node_name);
|
||||
if (node == nullptr) {
|
||||
/* The current tree path is invalid, probably because some parent group node has been
|
||||
* deleted. */
|
||||
return std::nullopt;
|
||||
}
|
||||
compute_context_builder.push<bke::NodeGroupComputeContext>(*node);
|
||||
}
|
||||
return compute_context_builder.hash();
|
||||
compute_context_builder.pop();
|
||||
}
|
||||
|
||||
GeoTreeLog *GeoModifierLog::get_tree_log_for_node_editor(const SpaceNode &snode)
|
||||
Map<const TreeZone *, ComputeContextHash> GeoModifierLog::get_context_hash_by_zone_for_node_editor(
|
||||
const SpaceNode &snode, StringRefNull modifier_name)
|
||||
{
|
||||
const Vector<const bNodeTreePath *> tree_path = snode.treepath;
|
||||
if (tree_path.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ComputeContextBuilder compute_context_builder;
|
||||
compute_context_builder.push<bke::ModifierComputeContext>(modifier_name);
|
||||
|
||||
for (const int i : tree_path.index_range().drop_back(1)) {
|
||||
bNodeTree *tree = tree_path[i]->nodetree;
|
||||
const char *group_node_name = tree_path[i + 1]->node_name;
|
||||
const bNode *group_node = nodeFindNodebyName(tree, group_node_name);
|
||||
if (group_node == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const TreeZones *tree_zones = tree->zones();
|
||||
if (tree_zones == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const Vector<const TreeZone *> zone_stack = tree_zones->get_zone_stack_for_node(
|
||||
group_node->identifier);
|
||||
for (const TreeZone *zone : zone_stack) {
|
||||
compute_context_builder.push<bke::SimulationZoneComputeContext>(*zone->output_node);
|
||||
}
|
||||
compute_context_builder.push<bke::NodeGroupComputeContext>(*group_node);
|
||||
}
|
||||
|
||||
const TreeZones *tree_zones = snode.edittree->zones();
|
||||
if (tree_zones == nullptr) {
|
||||
return {};
|
||||
}
|
||||
Map<const TreeZone *, ComputeContextHash> hash_by_zone;
|
||||
hash_by_zone.add_new(nullptr, compute_context_builder.hash());
|
||||
for (const TreeZone *zone : tree_zones->root_zones) {
|
||||
find_tree_zone_hash_recursive(*zone, compute_context_builder, hash_by_zone);
|
||||
}
|
||||
return hash_by_zone;
|
||||
}
|
||||
|
||||
Map<const TreeZone *, GeoTreeLog *> GeoModifierLog::get_tree_log_by_zone_for_node_editor(
|
||||
const SpaceNode &snode)
|
||||
{
|
||||
std::optional<ObjectAndModifier> object_and_modifier = get_modifier_for_node_editor(snode);
|
||||
if (!object_and_modifier) {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
GeoModifierLog *modifier_log = static_cast<GeoModifierLog *>(
|
||||
object_and_modifier->nmd->runtime_eval_log);
|
||||
if (modifier_log == nullptr) {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (const std::optional<ComputeContextHash> hash =
|
||||
GeoModifierLog::get_compute_context_hash_for_node_editor(
|
||||
snode, object_and_modifier->nmd->modifier.name))
|
||||
{
|
||||
return &modifier_log->get_tree_log(*hash);
|
||||
const Map<const TreeZone *, ComputeContextHash> hash_by_zone =
|
||||
GeoModifierLog::get_context_hash_by_zone_for_node_editor(
|
||||
snode, object_and_modifier->nmd->modifier.name);
|
||||
Map<const TreeZone *, GeoTreeLog *> log_by_zone;
|
||||
for (const auto item : hash_by_zone.items()) {
|
||||
GeoTreeLog &tree_log = modifier_log->get_tree_log(item.value);
|
||||
log_by_zone.add(item.key, &tree_log);
|
||||
}
|
||||
return nullptr;
|
||||
return log_by_zone;
|
||||
}
|
||||
|
||||
const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_path(const ViewerPath &viewer_path)
|
||||
@@ -587,8 +624,24 @@ const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_path(const ViewerP
|
||||
|
||||
ComputeContextBuilder compute_context_builder;
|
||||
compute_context_builder.push<bke::ModifierComputeContext>(parsed_path->modifier_name);
|
||||
for (const int32_t group_node_id : parsed_path->group_node_ids) {
|
||||
compute_context_builder.push<bke::NodeGroupComputeContext>(group_node_id);
|
||||
for (const ViewerPathElem *elem : parsed_path->node_path) {
|
||||
switch (elem->type) {
|
||||
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
|
||||
const auto &typed_elem = *reinterpret_cast<const GroupNodeViewerPathElem *>(elem);
|
||||
compute_context_builder.push<bke::NodeGroupComputeContext>(typed_elem.node_id);
|
||||
break;
|
||||
}
|
||||
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
|
||||
const auto &typed_elem = *reinterpret_cast<const SimulationZoneViewerPathElem *>(elem);
|
||||
compute_context_builder.push<bke::SimulationZoneComputeContext>(
|
||||
typed_elem.sim_output_node_id);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const ComputeContextHash context_hash = compute_context_builder.hash();
|
||||
nodes::geo_eval_log::GeoTreeLog &tree_log = modifier_log->get_tree_log(context_hash);
|
||||
|
||||
Reference in New Issue
Block a user