Files
test/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc
Hans Goudey d1df97bd48 Depsgraph: Use LinearAllocator to allocate Relations
I observed allocation becoming a bottleneck when building the depsgraph
with scenes with many simple data-blocks. One of the main culprits was
the struct that encodes relations between nodes in the graph.

Instead of allocating each `Relation` with a separate allocation call,
combine them into a `LinearAllocator`. That is must faster because it
allocates in large chunks and just bumps an offset on each allocation.

In a test file with 30 thousand cube objects, I observe a 1.18x
improvement in depsgraph evaluation time, from 1370 to 1163 ms.

The depsgraph isn't completely re-allocated when it's rebuilt, so the
allocator added in this PR has to be cleared manually. In the future,
it can be used for other structs or potentially strings.

Pull Request: https://projects.blender.org/blender/blender/pulls/137303
2025-04-11 17:14:07 +02:00

101 lines
2.8 KiB
C++

/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include <deque>
#include "intern/builder/deg_builder_remove_noop.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_operation.hh"
#include "intern/debug/deg_debug.h"
#include "intern/depsgraph.hh"
#include "intern/depsgraph_relation.hh"
#include "DEG_depsgraph_debug.hh"
namespace blender::deg {
static inline bool is_unused_noop(OperationNode *op_node)
{
if (op_node == nullptr) {
return false;
}
if (op_node->flag & OperationFlag::DEPSOP_FLAG_PINNED) {
return false;
}
return op_node->is_noop() && op_node->outlinks.is_empty();
}
static inline bool is_removable_relation(const Relation *relation)
{
if (relation->from->type != NodeType::OPERATION || relation->to->type != NodeType::OPERATION) {
return true;
}
const OperationNode *operation_from = static_cast<OperationNode *>(relation->from);
const OperationNode *operation_to = static_cast<OperationNode *>(relation->to);
/* If the relation connects two different IDs there is a high risk that the removal of the
* relation will make it so visibility flushing is not possible at runtime. This happens with
* relations like the DoF on camera of custom shape on bones: such relation do not lead to an
* actual depsgraph evaluation operation as they are handled on render engine level.
*
* The indirectly linked objects could have some of their components invisible as well, so
* also keep relations which connect different components of the same object so that visibility
* tracking happens correct in those cases as well. */
return operation_from->owner == operation_to->owner;
}
void deg_graph_remove_unused_noops(Depsgraph *graph)
{
std::deque<OperationNode *> queue;
for (OperationNode *node : graph->operations) {
if (is_unused_noop(node)) {
queue.push_back(node);
}
}
Vector<Relation *> relations_to_remove;
while (!queue.empty()) {
OperationNode *to_remove = queue.front();
queue.pop_front();
for (Relation *rel_in : to_remove->inlinks) {
if (!is_removable_relation(rel_in)) {
continue;
}
Node *dependency = rel_in->from;
relations_to_remove.append(rel_in);
/* Queue parent no-op node that has now become unused. */
OperationNode *operation = dependency->get_exit_operation();
if (is_unused_noop(operation)) {
queue.push_back(operation);
}
}
/* TODO(Sybren): Remove the node itself. */
}
/* Remove the relations. */
for (Relation *relation : relations_to_remove) {
relation->unlink();
}
DEG_DEBUG_PRINTF((::Depsgraph *)graph,
BUILD,
"Removed %d relations to no-op nodes\n",
int(relations_to_remove.size()));
}
} // namespace blender::deg