Geometry Nodes: speedup compute context hash generation
Whenever a node group is entered during evaluation, a new compute context is entered which has a corresponding hash. When node groups are entered and exited a lot, this can have some overhead. In my test file with ~100.000 node group invocations, this patch improves performance by about 7%. The speedup is achieved in two ways: * Avoid computing the same hash twice by caching it. * Invoke the hashing algorithm (md5 currently) only once instead of twice.
This commit is contained in:
parent
5dcce58510
commit
a09accb496
|
@ -6,6 +6,8 @@
|
|||
* This file implements some specific compute contexts for concepts in Blender.
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_compute_context.hh"
|
||||
|
||||
struct bNode;
|
||||
|
@ -41,7 +43,9 @@ class NodeGroupComputeContext : public ComputeContext {
|
|||
#endif
|
||||
|
||||
public:
|
||||
NodeGroupComputeContext(const ComputeContext *parent, int32_t node_id);
|
||||
NodeGroupComputeContext(const ComputeContext *parent,
|
||||
int32_t node_id,
|
||||
const std::optional<ComputeContextHash> &cached_hash = {});
|
||||
NodeGroupComputeContext(const ComputeContext *parent, const bNode &node);
|
||||
|
||||
int32_t node_id() const
|
||||
|
|
|
@ -19,11 +19,26 @@ void ModifierComputeContext::print_current_in_line(std::ostream &stream) const
|
|||
stream << "Modifier: " << modifier_name_;
|
||||
}
|
||||
|
||||
NodeGroupComputeContext::NodeGroupComputeContext(const ComputeContext *parent, const int node_id)
|
||||
NodeGroupComputeContext::NodeGroupComputeContext(
|
||||
const ComputeContext *parent,
|
||||
const int node_id,
|
||||
const std::optional<ComputeContextHash> &cached_hash)
|
||||
: ComputeContext(s_static_type, parent), node_id_(node_id)
|
||||
{
|
||||
hash_.mix_in(s_static_type, strlen(s_static_type));
|
||||
hash_.mix_in(&node_id_, sizeof(int32_t));
|
||||
if (cached_hash.has_value()) {
|
||||
hash_ = *cached_hash;
|
||||
}
|
||||
else {
|
||||
/* 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, &node_id_, sizeof(int32_t));
|
||||
hash_.mix_in(buffer, buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
NodeGroupComputeContext::NodeGroupComputeContext(const ComputeContext *parent, const bNode &node)
|
||||
|
|
|
@ -601,6 +601,12 @@ class LazyFunctionForGroupNode : public LazyFunction {
|
|||
std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_;
|
||||
std::optional<lf::GraphExecutor> graph_executor_;
|
||||
|
||||
struct Storage {
|
||||
void *graph_executor_storage = nullptr;
|
||||
/* To avoid computing the hash more than once. */
|
||||
std::optional<ComputeContextHash> context_hash_cache;
|
||||
};
|
||||
|
||||
public:
|
||||
LazyFunctionForGroupNode(const bNode &group_node,
|
||||
const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
|
||||
|
@ -662,9 +668,13 @@ class LazyFunctionForGroupNode : public LazyFunction {
|
|||
params.set_default_remaining_outputs();
|
||||
}
|
||||
|
||||
Storage *storage = static_cast<Storage *>(context.storage);
|
||||
|
||||
/* The compute context changes when entering a node group. */
|
||||
bke::NodeGroupComputeContext compute_context{user_data->compute_context,
|
||||
group_node_.identifier};
|
||||
bke::NodeGroupComputeContext compute_context{
|
||||
user_data->compute_context, group_node_.identifier, storage->context_hash_cache};
|
||||
storage->context_hash_cache = compute_context.hash();
|
||||
|
||||
GeoNodesLFUserData group_user_data = *user_data;
|
||||
group_user_data.compute_context = &compute_context;
|
||||
if (user_data->modifier_data->socket_log_contexts) {
|
||||
|
@ -674,18 +684,23 @@ class LazyFunctionForGroupNode : public LazyFunction {
|
|||
|
||||
lf::Context group_context = context;
|
||||
group_context.user_data = &group_user_data;
|
||||
group_context.storage = storage->graph_executor_storage;
|
||||
|
||||
graph_executor_->execute(params, group_context);
|
||||
}
|
||||
|
||||
void *init_storage(LinearAllocator<> &allocator) const override
|
||||
{
|
||||
return graph_executor_->init_storage(allocator);
|
||||
Storage *s = allocator.construct<Storage>().release();
|
||||
s->graph_executor_storage = graph_executor_->init_storage(allocator);
|
||||
return s;
|
||||
}
|
||||
|
||||
void destruct_storage(void *storage) const override
|
||||
{
|
||||
graph_executor_->destruct_storage(storage);
|
||||
Storage *s = static_cast<Storage *>(storage);
|
||||
graph_executor_->destruct_storage(s->graph_executor_storage);
|
||||
std::destroy_at(s);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue