Compositor: Implement per-node execution time report #117885

Merged
Sergey Sharybin merged 7 commits from Sergey/blender:compositor_timings into main 2024-02-09 10:19:34 +01:00
20 changed files with 393 additions and 33 deletions

View File

@ -867,6 +867,9 @@ class NODE_PT_overlay(Panel):
col.prop(overlay, "show_timing", text="Timings")
col.prop(overlay, "show_named_attributes", text="Named Attributes")
if snode.tree_type == 'CompositorNodeTree':
col.prop(overlay, "show_timing", text="Timings")
class NODE_MT_node_tree_interface_context_menu(Menu):
bl_label = "Node Tree Interface Specials"

View File

@ -4,12 +4,22 @@
#pragma once
#include "BLI_map.hh"
#include "BLI_timeit.hh"
#include "BLI_utility_mixins.hh"
namespace blender::bke {
/* Runtime data specific to the compositing trees. */
struct CompositorRuntime {
/* Per-node instance total execution time for the corresponding node, during the last tree
* evaluation. */
Map<bNodeInstanceKey, timeit::Nanoseconds> per_node_execution_time;
};
class SceneRuntime : NonCopyable, NonMovable {
public:
CompositorRuntime compositor;
};
} // namespace blender::bke

View File

@ -35,6 +35,7 @@ if(WITH_COMPOSITOR_CPU)
set(SRC
COM_compositor.hh
COM_defines.h
COM_profile.hh
intern/COM_BufferArea.h
intern/COM_BufferOperation.cc
@ -100,6 +101,7 @@ if(WITH_COMPOSITOR_CPU)
intern/COM_WorkScheduler.cc
intern/COM_WorkScheduler.h
intern/COM_compositor.cc
intern/COM_profile.cc
operations/COM_QualityStepHelper.cc
operations/COM_QualityStepHelper.h

View File

@ -10,6 +10,9 @@
namespace blender::realtime_compositor {
class RenderContext;
}
namespace blender::compositor {
class ProfilerData;
}
struct Render;
@ -325,7 +328,7 @@ struct Render;
* should be checked further, probably it'll be also needed for preview
* generation in display space
*/
/* clang-format off */
/* clang-format on */
void COM_execute(Render *render,
RenderData *render_data,
@ -333,7 +336,8 @@ void COM_execute(Render *render,
bNodeTree *node_tree,
bool rendering,
const char *view_name,
blender::realtime_compositor::RenderContext *render_context);
blender::realtime_compositor::RenderContext *render_context,
blender::compositor::ProfilerData &profiler_data);
/**
* \brief Deinitialize the compositor caches and allocated memory.

View File

@ -0,0 +1,56 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_map.hh"
#include "BLI_timeit.hh"
#include "DNA_node_types.h"
struct bNodeTree;
namespace blender::compositor {
class NodeOperation;
/* Profiling ddata gathered during execution of a compositing node tree. */
class ProfilerData {
public:

break->broken

break->broken
/* Per-node accumulated execution time. Includes execution time of all operations the node was
* broken down into. */
Map<bNodeInstanceKey, timeit::Nanoseconds> per_node_execution_time;
};
/* Profiler implementation which is used by the node execution system. */
class Profiler {
/* Local copy of the profiling data, which is known to not cause threading conflicts with the
* interface thread while the compositing tree is evaluated in the background. */

in background -> in the background

`in background` -> `in the background`
ProfilerData data_;
public:
void add_operation_execution_time(const NodeOperation &operation,
const timeit::TimePoint &start,
const timeit::TimePoint &end);
void finalize(const bNodeTree &node_tree);
const ProfilerData &get_data() const
{
return data_;
}
private:
/* Add execution time to the node denoted by its key. */
void add_execution_time(const bNodeInstanceKey parent_key,
const timeit::Nanoseconds &execution_time);
/* Accumulate execution time of the group node instances, and store their execution time in the
* per_node_execution_time_.
* Returns total execution time of the given node tree. */
timeit::Nanoseconds accumulate_node_group_times(
const bNodeTree &node_tree, const bNodeInstanceKey parent_key = NODE_INSTANCE_KEY_BASE);
};
} // namespace blender::compositor

View File

@ -12,11 +12,14 @@
# include "MEM_guardedalloc.h"
#endif
#include "COM_profile.hh"
namespace blender::compositor {
class CompositorContext;
class ExecutionSystem;
class NodeOperation;
class ProfilerData;
/**
* Base class for execution models. Contains shared implementation.
@ -43,6 +46,8 @@ class ExecutionModel {
*/
Span<NodeOperation *> operations_;
Profiler profiler_;
public:
ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations);
@ -50,6 +55,11 @@ class ExecutionModel {
virtual void execute(ExecutionSystem &exec_system) = 0;
const ProfilerData &get_profiler_data() const
{
return profiler_.get_data();
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel")
#endif

View File

@ -25,7 +25,9 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
bool rendering,
bool fastcalculation,
const char *view_name,
realtime_compositor::RenderContext *render_context)
realtime_compositor::RenderContext *render_context,
ProfilerData &profiler_data)
: profiler_data_(profiler_data)
{
num_work_threads_ = WorkScheduler::get_num_cpu_threads();
context_.set_render_context(render_context);
@ -100,6 +102,8 @@ void ExecutionSystem::execute()
op->init_data();
}
execution_model_->execute(*this);
profiler_data_ = execution_model_->get_profiler_data();
}
void ExecutionSystem::execute_work(const rcti &work_rect,

View File

@ -26,6 +26,8 @@ class RenderContext;
namespace blender::compositor {
class ProfilerData;
/**
* \page execution Execution model
* In order to get to an efficient model for execution, several steps are being done. these steps
@ -152,6 +154,8 @@ class ExecutionSystem {
ThreadMutex work_mutex_;
ThreadCondition work_finished_cond_;
ProfilerData &profiler_data_;
public:
/**
* \brief Create a new ExecutionSystem and initialize it with the
@ -166,7 +170,8 @@ class ExecutionSystem {
bool rendering,
bool fastcalculation,
const char *view_name,
realtime_compositor::RenderContext *render_context);
realtime_compositor::RenderContext *render_context,
ProfilerData &profiler_data);
/**
* Destructor

View File

@ -12,6 +12,8 @@
#include "COM_ViewerOperation.h"
#include "COM_WorkScheduler.h"
#include "BLI_timeit.hh"
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
#endif
@ -42,6 +44,8 @@ void FullFrameExecutionModel::execute(ExecutionSystem &exec_system)
determine_areas_to_render_and_reads();
render_operations();
profiler_.finalize(*node_tree);
}
void FullFrameExecutionModel::determine_areas_to_render_and_reads()
@ -101,6 +105,8 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op)
constexpr int output_x = 0;
constexpr int output_y = 0;
const timeit::TimePoint time_start = timeit::Clock::now();
const bool has_outputs = op->get_number_of_output_sockets() > 0;
MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr;
if (op->get_width() > 0 && op->get_height() > 0) {
@ -120,6 +126,8 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op)
active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf));
operation_finished(op);
profiler_.add_operation_execution_time(*op, time_start, timeit::Clock::now());
}
void FullFrameExecutionModel::render_operations()

View File

@ -19,6 +19,7 @@
#include "COM_MemoryBuffer.h"
#include "COM_MetaData.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "clew.h"
@ -310,6 +311,8 @@ class NodeOperation {
private:
int id_;
std::string name_;
bNodeInstanceKey node_instance_key_{NODE_INSTANCE_KEY_NONE};
Vector<NodeOperationInput> inputs_;
Vector<NodeOperationOutput> outputs_;
@ -377,6 +380,15 @@ class NodeOperation {
return id_;
}
const void set_node_instance_key(const bNodeInstanceKey &node_instance_key)
{
node_instance_key_ = node_instance_key;
}
const bNodeInstanceKey get_node_instance_key() const
{
return node_instance_key_;
}
/** Get constant value when operation is constant, otherwise return default_value. */
float get_constant_value_default(float default_value);
/** Get constant elem when operation is constant, otherwise return default_elem. */

View File

@ -124,6 +124,7 @@ void NodeOperationBuilder::add_operation(NodeOperation *operation)
operations_.append(operation);
if (current_node_) {
operation->set_name(current_node_->get_bnode()->name);
operation->set_node_instance_key(current_node_->get_instance_key());
}
operation->set_execution_model(context_->get_execution_model());
operation->set_execution_system(exec_system_);

View File

@ -55,7 +55,8 @@ void COM_execute(Render *render,
bNodeTree *node_tree,
bool rendering,
const char *view_name,
blender::realtime_compositor::RenderContext *render_context)
blender::realtime_compositor::RenderContext *render_context,
blender::compositor::ProfilerData &profiler_data)
{
/* Initialize mutex, TODO: this mutex init is actually not thread safe and
* should be done somewhere as part of blender startup, all the other
@ -95,8 +96,14 @@ void COM_execute(Render *render,
/* Execute. */
const bool twopass = (node_tree->flag & NTREE_TWO_PASS) && !rendering;
if (twopass) {
blender::compositor::ExecutionSystem fast_pass(
render_data, scene, node_tree, rendering, true, view_name, render_context);
blender::compositor::ExecutionSystem fast_pass(render_data,
scene,
node_tree,
rendering,
true,
view_name,
render_context,
profiler_data);
fast_pass.execute();
if (node_tree->runtime->test_break(node_tree->runtime->tbh)) {
@ -106,7 +113,7 @@ void COM_execute(Render *render,
}
blender::compositor::ExecutionSystem system(
render_data, scene, node_tree, rendering, false, view_name, render_context);
render_data, scene, node_tree, rendering, false, view_name, render_context, profiler_data);
system.execute();
}

View File

@ -0,0 +1,79 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "COM_profile.hh"
#include "BKE_node_runtime.hh"
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
namespace blender::compositor {
void Profiler::add_operation_execution_time(const NodeOperation &operation,
const timeit::TimePoint &start,
const timeit::TimePoint &end)
{
const timeit::Nanoseconds execution_time = end - start;
const bNodeInstanceKey key = operation.get_node_instance_key();
if (key.value == NODE_INSTANCE_KEY_NONE.value) {
/* The operation does not come from any node. It was, for example, added to convert data type.
* Do not accumulate time from its execution. */
return;
}
this->add_execution_time(key, execution_time);
}
void Profiler::add_execution_time(const bNodeInstanceKey key,
const timeit::Nanoseconds &execution_time)
{
data_.per_node_execution_time.add_or_modify(
key,
[&](timeit::Nanoseconds *total_execution_time) { *total_execution_time = execution_time; },
[&](timeit::Nanoseconds *total_execution_time) { *total_execution_time += execution_time; });
}
void Profiler::finalize(const bNodeTree &node_tree)
{
this->accumulate_node_group_times(node_tree);

this->accumulate_node_group_times

`this->accumulate_node_group_times`
}
timeit::Nanoseconds Profiler::accumulate_node_group_times(const bNodeTree &node_tree,
const bNodeInstanceKey parent_key)
{
timeit::Nanoseconds tree_execution_time(0);
for (const bNode *node : node_tree.all_nodes()) {
const bNodeInstanceKey key = BKE_node_instance_key(parent_key, &node_tree, node);
if (node->type != NODE_GROUP) {
/* Non-group node, no need to recurse into. Simply accumulate the node's execution time to

recurs -> recurse

`recurs` -> `recurse`
* the current tree's execution time. */
tree_execution_time += data_.per_node_execution_time.lookup_default(key,
timeit::Nanoseconds(0));
continue;
}
if (node->id == nullptr) {
/* Node group has lost link to its node tree. For example, due to missing linked file. */
continue;
}
const timeit::Nanoseconds group_execution_time = this->accumulate_node_group_times(
*reinterpret_cast<const bNodeTree *>(node->id), key);
/* Store execution time of the group node. */
this->add_execution_time(key, group_execution_time);
/* Add group execution time to the overall tree execution time. */
tree_execution_time += group_execution_time;
}
return tree_execution_time;
}
} // namespace blender::compositor

View File

@ -47,6 +47,7 @@
#include "BKE_node_tree_zones.hh"
#include "BKE_object.hh"
#include "BKE_scene.h"
#include "BKE_scene_runtime.hh"
#include "BKE_type_conversions.hh"
#include "IMB_imbuf.hh"
@ -92,6 +93,8 @@
#include "GEO_fillet_curves.hh"
#include "COM_profile.hh"
#include "node_intern.hh" /* own include */
#include <fmt/format.h>
@ -126,6 +129,9 @@ struct TreeDrawContext {
* True if there is an active realtime compositor using the node tree, false otherwise.
*/
bool used_by_realtime_compositor = false;
blender::Map<bNodeInstanceKey, blender::timeit::Nanoseconds>
*compositor_per_node_execution_time = nullptr;
};
float ED_node_grid_size()
@ -2408,9 +2414,11 @@ static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx,
UI_block_emboss_set(&block, UI_EMBOSS);
}
static std::optional<std::chrono::nanoseconds> node_get_execution_time(
const TreeDrawContext &tree_draw_ctx, const bNodeTree &ntree, const bNode &node)
static std::optional<std::chrono::nanoseconds> geo_node_get_execution_time(
const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
{
const bNodeTree &ntree = *snode.edittree;
geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * {
const bNodeTreeZones *zones = ntree.zones();
if (!zones) {
@ -2433,8 +2441,8 @@ static std::optional<std::chrono::nanoseconds> node_get_execution_time(
for (const bNode *tnode : node.direct_children_in_frame()) {
if (tnode->is_frame()) {
std::optional<std::chrono::nanoseconds> sub_frame_run_time = node_get_execution_time(
tree_draw_ctx, ntree, *tnode);
std::optional<std::chrono::nanoseconds> sub_frame_run_time = geo_node_get_execution_time(
tree_draw_ctx, snode, *tnode);
if (sub_frame_run_time.has_value()) {
run_time += *sub_frame_run_time;
found_node = true;
@ -2459,12 +2467,87 @@ static std::optional<std::chrono::nanoseconds> node_get_execution_time(
return std::nullopt;
}
/* Create node key instance, assuming the node comes from the currently edited node tree. */

editing -> edited

`editing` -> `edited`
static bNodeInstanceKey current_node_instance_key(const SpaceNode &snode, const bNode &node)
{
const bNodeTreePath *path = static_cast<const bNodeTreePath *>(snode.treepath.last);
/* Some code in this file checks for the non-null elements of the tree path. However, if we did
* iterate into a node it is expected that there is a tree, and it should be in the path.
* Otherwise something else went wrong. */
BLI_assert(path);
/* Assume that the currently editing tree is the last in the path. */
BLI_assert(snode.edittree == path->nodetree);
return BKE_node_instance_key(path->parent_key, snode.edittree, &node);
}
static std::optional<std::chrono::nanoseconds> compositor_accumulate_frame_node_execution_time(
const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
{
BLI_assert(tree_draw_ctx.compositor_per_node_execution_time);
timeit::Nanoseconds frame_execution_time(0);
bool has_any_execution_time = false;
for (const bNode *current_node : node.direct_children_in_frame()) {
const bNodeInstanceKey key = current_node_instance_key(snode, *current_node);
if (const timeit::Nanoseconds *node_execution_time =
tree_draw_ctx.compositor_per_node_execution_time->lookup_ptr(key))
{
frame_execution_time += *node_execution_time;
has_any_execution_time = true;
}
}
if (!has_any_execution_time) {
return std::nullopt;
}
return frame_execution_time;
}
static std::optional<std::chrono::nanoseconds> compositor_node_get_execution_time(
const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
{
BLI_assert(tree_draw_ctx.compositor_per_node_execution_time);
/* For the frame nodes accumulate execution time of its children. */
if (node.is_frame()) {
return compositor_accumulate_frame_node_execution_time(tree_draw_ctx, snode, node);
}
/* For other nodes simply lookup execution time.
* The group node instances have their own entries in the execution times map. */
const bNodeInstanceKey key = current_node_instance_key(snode, node);
if (const timeit::Nanoseconds *execution_time =
tree_draw_ctx.compositor_per_node_execution_time->lookup_ptr(key))
{
return *execution_time;
}
return std::nullopt;
}
static std::optional<std::chrono::nanoseconds> node_get_execution_time(
const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
{
switch (snode.edittree->type) {
case NTREE_GEOMETRY:
return geo_node_get_execution_time(tree_draw_ctx, snode, node);
case NTREE_COMPOSIT:
return compositor_node_get_execution_time(tree_draw_ctx, snode, node);
}
return std::nullopt;
}
static std::string node_get_execution_time_label(TreeDrawContext &tree_draw_ctx,
const SpaceNode &snode,
const bNode &node)
{
const std::optional<std::chrono::nanoseconds> exec_time = node_get_execution_time(
tree_draw_ctx, *snode.edittree, node);
tree_draw_ctx, snode, node);
if (!exec_time.has_value()) {
return std::string("");
@ -2603,6 +2686,35 @@ static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(
return row_from_used_named_attribute(node_log->used_named_attributes);
}
static std::optional<NodeExtraInfoRow> node_get_execution_time_label_row(
TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
{
NodeExtraInfoRow row;
row.text = node_get_execution_time_label(tree_draw_ctx, snode, node);
if (row.text.empty()) {
return std::nullopt;
}
row.tooltip = TIP_(
"The execution time from the node tree's latest evaluation. For frame and group "
"nodes, the time for all sub-nodes");
row.icon = ICON_PREVIEW_RANGE;
return row;
}
static void node_get_compositor_extra_info(TreeDrawContext &tree_draw_ctx,
const SpaceNode &snode,
const bNode &node,
Vector<NodeExtraInfoRow> &rows)
{
if (snode.overlay.flag & SN_OVERLAY_SHOW_TIMINGS) {
std::optional<NodeExtraInfoRow> row = node_get_execution_time_label_row(
tree_draw_ctx, snode, node);
if (row.has_value()) {
rows.append(std::move(*row));
}
}
}
static Vector<NodeExtraInfoRow> node_get_extra_info(const bContext &C,
TreeDrawContext &tree_draw_ctx,
const SpaceNode &snode,
@ -2623,8 +2735,13 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const bContext &C,
rows.append(std::move(row));
}
if (snode.edittree->type == NTREE_COMPOSIT) {
node_get_compositor_extra_info(tree_draw_ctx, snode, node, rows);
return rows;
}

infos->info

infos->info
if (!(snode.edittree->type == NTREE_GEOMETRY)) {
/* Currently geometry nodes are the only nodes to have extra infos per nodes. */
/* Currently geometry and compositor nodes are the only nodes to have extra info per nodes. */
return rows;
}
@ -2640,15 +2757,10 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const bContext &C,
(ELEM(node.typeinfo->nclass, NODE_CLASS_GEOMETRY, NODE_CLASS_GROUP, NODE_CLASS_ATTRIBUTE) ||
ELEM(node.type, NODE_FRAME, NODE_GROUP_OUTPUT)))
{
NodeExtraInfoRow row;
row.text = node_get_execution_time_label(tree_draw_ctx, snode, node);
if (!row.text.empty()) {
row.tooltip = TIP_(
"The execution time from the node tree's latest evaluation. For frame and group "
"nodes, "
"the time for all sub-nodes");
row.icon = ICON_PREVIEW_RANGE;
rows.append(std::move(row));
std::optional<NodeExtraInfoRow> row = node_get_execution_time_label_row(
tree_draw_ctx, snode, node);
if (row.has_value()) {
rows.append(std::move(*row));
}
}
@ -4140,7 +4252,10 @@ static void draw_nodetree(const bContext &C,
workspace->viewer_path, *snode);
}
else if (ntree.type == NTREE_COMPOSIT) {
const Scene *scene = CTX_data_scene(&C);
tree_draw_ctx.used_by_realtime_compositor = realtime_compositor_is_in_use(C);
tree_draw_ctx.compositor_per_node_execution_time =
&scene->runtime->compositor.per_node_execution_time;
}
else if (ntree.type == NTREE_SHADER && U.experimental.use_shader_node_previews &&
BKE_scene_uses_shader_previews(CTX_data_scene(&C)) &&

View File

@ -29,6 +29,7 @@
#include "BKE_node_tree_update.hh"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_scene_runtime.hh"
#include "BKE_workspace.h"
#include "BLI_set.hh"
@ -74,6 +75,8 @@
#include "NOD_texture.h"
#include "node_intern.hh" /* own include */
#include "COM_profile.hh"
namespace blender::ed::space_node {
#define USE_ESC_COMPO
@ -104,6 +107,8 @@ struct CompoJob {
bool *do_update;
float *progress;
bool cancelled;
blender::compositor::ProfilerData profiler_data;
};
float node_socket_calculate_height(const bNodeSocket &socket)
@ -223,7 +228,8 @@ static void compo_freejob(void *cjv)
if (cj->compositor_depsgraph != nullptr) {
DEG_graph_free(cj->compositor_depsgraph);
}
MEM_freeN(cj);
MEM_delete(cj);
}
/* Only now we copy the nodetree, so adding many jobs while
@ -296,15 +302,23 @@ static void compo_startjob(void *cjv, wmJobWorkerStatus *worker_status)
BKE_callback_exec_id(cj->bmain, &scene->id, BKE_CB_EVT_COMPOSITE_PRE);
if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) {
ntreeCompositExecTree(cj->re, cj->scene, ntree, &cj->scene->r, false, true, "", nullptr);
ntreeCompositExecTree(
cj->re, cj->scene, ntree, &cj->scene->r, false, true, "", nullptr, cj->profiler_data);
}
else {
LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) {
continue;
}
ntreeCompositExecTree(
cj->re, cj->scene, ntree, &cj->scene->r, false, true, srv->name, nullptr);
ntreeCompositExecTree(cj->re,
cj->scene,
ntree,
&cj->scene->r,
false,
true,
srv->name,
nullptr,
cj->profiler_data);
}
}
OmarEmaraDev marked this conversation as resolved Outdated

When adjusting parameters in the node tree and it gets repeatedly canceled, this will display <1ms. So perhaps we shouldn't update the timings for canceled executions?

When adjusting parameters in the node tree and it gets repeatedly canceled, this will display `<1ms`. So perhaps we shouldn't update the timings for canceled executions?

That is a good question. I can see this could be useful when quickly tweaking something. But in a case when execution takes a long time, and you stopped it (Esc, or via the status bar) it might be interesting and useful to see which nodes took the longes so far.

That is a good question. I can see this could be useful when quickly tweaking something. But in a case when execution takes a long time, and you stopped it (Esc, or via the status bar) it might be interesting and useful to see which nodes took the longes so far.

Aha, that will be useful indeed, then it is fine.

Aha, that will be useful indeed, then it is fine.
@ -320,6 +334,8 @@ static void compo_canceljob(void *cjv)
Scene *scene = cj->scene;
BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_COMPOSITE_CANCEL);
cj->cancelled = true;
scene->runtime->compositor.per_node_execution_time = cj->profiler_data.per_node_execution_time;
}
static void compo_completejob(void *cjv)
@ -328,6 +344,8 @@ static void compo_completejob(void *cjv)
Main *bmain = cj->bmain;
Scene *scene = cj->scene;
BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_COMPOSITE_POST);
scene->runtime->compositor.per_node_execution_time = cj->profiler_data.per_node_execution_time;
}
/** \} */
@ -395,7 +413,7 @@ void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_
"Compositing",
WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS,
WM_JOB_TYPE_COMPOSITE);
CompoJob *cj = MEM_cnew<CompoJob>("compo job");
CompoJob *cj = MEM_new<CompoJob>("compo job");
/* Custom data for preview thread. */
cj->bmain = bmain;

View File

@ -539,6 +539,22 @@ enum {
*/
typedef struct bNodeInstanceKey {
unsigned int value;
#ifdef __cplusplus
inline bool operator==(const bNodeInstanceKey &other) const
{
return value == other.value;
}
inline bool operator!=(const bNodeInstanceKey &other) const
{
return !(*this == other);
}
inline uint64_t hash() const
{
return value;
}
#endif
} bNodeInstanceKey;
/**

View File

@ -13,6 +13,9 @@
namespace blender::realtime_compositor {
class RenderContext;
}
namespace blender::compositor {
class ProfilerData;
}
struct bNodeTreeType;
struct CryptomatteSession;
@ -41,7 +44,8 @@ void ntreeCompositExecTree(Render *render,
bool rendering,
int do_previews,
const char *view_name,
blender::realtime_compositor::RenderContext *render_context);
blender::realtime_compositor::RenderContext *render_context,
blender::compositor::ProfilerData &profiler_data);
/**
* Called from render pipeline, to tag render input and output.

View File

@ -180,12 +180,13 @@ void ntreeCompositExecTree(Render *render,
bool rendering,
int do_preview,
const char *view_name,
blender::realtime_compositor::RenderContext *render_context)
blender::realtime_compositor::RenderContext *render_context,
blender::compositor::ProfilerData &profiler_data)
{
#ifdef WITH_COMPOSITOR_CPU
COM_execute(render, rd, scene, ntree, rendering, view_name, render_context);
COM_execute(render, rd, scene, ntree, rendering, view_name, render_context, profiler_data);
#else
UNUSED_VARS(render, scene, ntree, rd, rendering, view_name, render_context);
UNUSED_VARS(render, scene, ntree, rd, rendering, view_name, render_context, profiler_data);
#endif
UNUSED_VARS(do_preview);

View File

@ -8,6 +8,7 @@ set(INC
intern
../blenkernel
../blentranslation
../compositor
../compositor/realtime_compositor
../compositor/realtime_compositor/cached_resources
../draw
@ -64,6 +65,7 @@ set(LIB
PRIVATE bf::depsgraph
PRIVATE bf::dna
PRIVATE bf::intern::guardedalloc
bf_compositor
bf_realtime_compositor
PRIVATE bf::intern::atomic
)

View File

@ -67,6 +67,7 @@
#include "NOD_composite.hh"
#include "COM_profile.hh"
#include "COM_render_context.hh"
#include "DEG_depsgraph.hh"
@ -1301,6 +1302,7 @@ static void do_render_compositor(Render *re)
}
blender::realtime_compositor::RenderContext compositor_render_context;
blender::compositor::ProfilerData profiler_data;
LISTBASE_FOREACH (RenderView *, rv, &re->result->views) {
ntreeCompositExecTree(re,
re->pipeline_scene_eval,
@ -1309,7 +1311,8 @@ static void do_render_compositor(Render *re)
true,
G.background == 0,
rv->name,
&compositor_render_context);
&compositor_render_context,
profiler_data);
}
compositor_render_context.save_file_outputs(re->pipeline_scene_eval);