From e53ac805af5d9682edd8738063838704145be870 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Tue, 4 Feb 2025 08:34:48 +0100 Subject: [PATCH] Compositor: Avoid redundant output computations This patch allows the compositor context to specify exactly which outputs it needs, selecting from: Composite, Viewer, File Output, and Previews. Previously, the compositor fully executed if any of those were needed, without granular control on which outputs are needed exactly. For the viewport compositor engine, it requests Composite and Viewer, with no Previews or File Outputs. For the render pipeline, it requests Composite and File Output, with node Viewer or Previews. For the interactive compositor, it requests Viewer if the backdrop is visible or an image editor with the viewer image is visible, it requests Compositor if an image editor with the render result is visible, it requests Previews if a node editor has previews overlay enabled. File outputs are never requested. Pull Request: https://projects.blender.org/blender/blender/pulls/133960 --- source/blender/compositor/COM_compositor.hh | 6 +- source/blender/compositor/COM_context.hh | 19 ++++-- .../compositor/intern/COM_compositor.cc | 13 +++- .../compositor/intern/node_operation.cc | 2 +- source/blender/compositor/intern/scheduler.cc | 26 +++++--- .../engines/compositor/compositor_engine.cc | 9 +-- .../blender/editors/space_node/CMakeLists.txt | 4 ++ .../blender/editors/space_node/node_edit.cc | 63 ++++++++++++++++++- .../blender/editors/space_node/space_node.cc | 28 +-------- source/blender/render/RE_compositor.hh | 6 +- source/blender/render/intern/compositor.cc | 27 ++++---- source/blender/render/intern/pipeline.cc | 5 +- source/blender/render/intern/render_types.h | 10 ++- 13 files changed, 144 insertions(+), 74 deletions(-) diff --git a/source/blender/compositor/COM_compositor.hh b/source/blender/compositor/COM_compositor.hh index 89d26243bd4..c00616614f1 100644 --- a/source/blender/compositor/COM_compositor.hh +++ b/source/blender/compositor/COM_compositor.hh @@ -4,11 +4,14 @@ #pragma once +#include + #include "DNA_node_types.h" namespace blender::compositor { class RenderContext; class Profiler; +enum class OutputTypes : uint8_t; } // namespace blender::compositor struct Render; @@ -52,7 +55,8 @@ void COM_execute(Render *render, bNodeTree *node_tree, const char *view_name, blender::compositor::RenderContext *render_context, - blender::compositor::Profiler *profiler); + blender::compositor::Profiler *profiler, + blender::compositor::OutputTypes needed_outputs); /** * \brief Deinitialize the compositor caches and allocated memory. diff --git a/source/blender/compositor/COM_context.hh b/source/blender/compositor/COM_context.hh index 48b0690a94c..bdfa37e8af1 100644 --- a/source/blender/compositor/COM_context.hh +++ b/source/blender/compositor/COM_context.hh @@ -4,6 +4,8 @@ #pragma once +#include + #include "BLI_math_vector_types.hh" #include "BLI_string_ref.hh" @@ -23,6 +25,16 @@ namespace blender::compositor { +/* Enumerates the possible outputs that the compositor can compute. */ +enum class OutputTypes : uint8_t { + None = 0, + Composite = 1 << 0, + Viewer = 1 << 1, + FileOutput = 1 << 2, + Previews = 1 << 3, +}; +ENUM_OPERATORS(OutputTypes, OutputTypes::Previews) + /* ------------------------------------------------------------------------------------------------ * Context * @@ -57,11 +69,8 @@ class Context { * denoising quality on a node. */ virtual eCompositorDenoiseQaulity get_denoise_quality() const = 0; - /* True if the compositor should write file outputs, false otherwise. */ - virtual bool use_file_output() const = 0; - - /* True if the compositor should compute node previews, false otherwise. */ - virtual bool should_compute_node_previews() const = 0; + /* Returns all output types that should be computed. */ + virtual OutputTypes needed_outputs() const = 0; /* Get the render settings for compositing. */ virtual const RenderData &get_render_data() const = 0; diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc index 0b9b4cbafb5..7c1927f4ebb 100644 --- a/source/blender/compositor/intern/COM_compositor.cc +++ b/source/blender/compositor/intern/COM_compositor.cc @@ -54,7 +54,8 @@ void COM_execute(Render *render, bNodeTree *node_tree, const char *view_name, blender::compositor::RenderContext *render_context, - blender::compositor::Profiler *profiler) + blender::compositor::Profiler *profiler, + blender::compositor::OutputTypes needed_outputs) { /* Initialize mutex, TODO: this mutex init is actually not thread safe and * should be done somewhere as part of blender startup, all the other @@ -76,8 +77,14 @@ void COM_execute(Render *render, compositor_init_node_previews(render_data, node_tree); compositor_reset_node_tree_status(node_tree); - RE_compositor_execute( - *render, *scene, *render_data, *node_tree, view_name, render_context, profiler); + RE_compositor_execute(*render, + *scene, + *render_data, + *node_tree, + view_name, + render_context, + profiler, + needed_outputs); BLI_mutex_unlock(&g_compositor.mutex); } diff --git a/source/blender/compositor/intern/node_operation.cc b/source/blender/compositor/intern/node_operation.cc index 95833064968..c2011e22e63 100644 --- a/source/blender/compositor/intern/node_operation.cc +++ b/source/blender/compositor/intern/node_operation.cc @@ -50,7 +50,7 @@ void NodeOperation::evaluate() void NodeOperation::compute_preview() { - if (context().should_compute_node_previews() && is_node_preview_needed(node())) { + if (bool(context().needed_outputs() & OutputTypes::Previews) && is_node_preview_needed(node())) { compositor::compute_preview(context(), node(), *get_preview_result()); } } diff --git a/source/blender/compositor/intern/scheduler.cc b/source/blender/compositor/intern/scheduler.cc index a43667193c4..66a34360396 100644 --- a/source/blender/compositor/intern/scheduler.cc +++ b/source/blender/compositor/intern/scheduler.cc @@ -91,23 +91,29 @@ static void add_output_nodes(const Context &context, const DTreeContext &root_context = tree.root_context(); /* Only add File Output nodes if the context supports them. */ - if (context.use_file_output()) { + if (bool(context.needed_outputs() & OutputTypes::FileOutput)) { add_file_output_nodes(root_context, node_stack); } - /* Add the active composite node in the root tree, but only if we are not treating viewer outputs - * as composite ones. That's because in cases where viewer nodes will be treated as composite - * outputs, viewer nodes will take precedence, so this is handled as a special case in the - * add_viewer_nodes_in_context function instead and no need to add it here. */ - if (!context.treat_viewer_as_composite_output()) { - for (const bNode *node : root_context.btree().nodes_by_type("CompositorNodeComposite")) { - if (node->flag & NODE_DO_OUTPUT && !node->is_muted()) { - node_stack.push(DNode(&root_context, node)); - break; + /* Add the active composite node in the root tree if needed, but only if we are not treating + * viewer outputs as composite ones. That's because in cases where viewer nodes will be treated + * as composite outputs, viewer nodes will take precedence, so this is handled as a special case + * in the add_viewer_nodes_in_context function instead and no need to add it here. */ + if (bool(context.needed_outputs() & OutputTypes::Composite)) { + if (!context.treat_viewer_as_composite_output()) { + for (const bNode *node : root_context.btree().nodes_by_type("CompositorNodeComposite")) { + if (node->flag & NODE_DO_OUTPUT && !node->is_muted()) { + node_stack.push(DNode(&root_context, node)); + break; + } } } } + if (!bool(context.needed_outputs() & OutputTypes::Viewer)) { + return; + } + const DTreeContext &active_context = tree.active_context(); const bool viewer_was_added = add_viewer_nodes_in_context(context, &active_context, node_stack); diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc index 626ca1a4c9e..9a0327b8ff5 100644 --- a/source/blender/draw/engines/compositor/compositor_engine.cc +++ b/source/blender/draw/engines/compositor/compositor_engine.cc @@ -82,14 +82,9 @@ class Context : public compositor::Context { this->get_render_data().compositor_denoise_preview_quality); } - bool use_file_output() const override + compositor::OutputTypes needed_outputs() const override { - return false; - } - - bool should_compute_node_previews() const override - { - return false; + return compositor::OutputTypes::Composite | compositor::OutputTypes::Viewer; } /* The viewport compositor does not support viewer outputs, so treat viewers as composite diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index c55e0b2085d..3c8d68ee262 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -6,6 +6,10 @@ set(INC ../include ../io ../../compositor + ../../compositor/algorithms + ../../compositor/cached_resources + ../../compositor/derived_resources + ../../compositor/utilities ../../makesrna # RNA_prototypes.hh diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 63d8f2e61c3..92a1046aeed 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -75,6 +75,7 @@ #include "node_intern.hh" /* own include */ #include "COM_compositor.hh" +#include "COM_context.hh" #include "COM_profiler.hh" namespace blender::ed::space_node { @@ -109,6 +110,7 @@ struct CompoJob { bool cancelled; compositor::Profiler profiler; + compositor::OutputTypes needed_outputs; }; float node_socket_calculate_height(const bNodeSocket &socket) @@ -308,14 +310,15 @@ static void compo_startjob(void *cjv, wmJobWorkerStatus *worker_status) BKE_callback_exec_id(cj->bmain, &cj->scene->id, BKE_CB_EVT_COMPOSITE_PRE); if ((scene->r.scemode & R_MULTIVIEW) == 0) { - COM_execute(cj->re, &scene->r, scene, ntree, "", nullptr, &cj->profiler); + COM_execute(cj->re, &scene->r, scene, ntree, "", nullptr, &cj->profiler, cj->needed_outputs); } else { LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) { if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) { continue; } - COM_execute(cj->re, &scene->r, scene, ntree, srv->name, nullptr, &cj->profiler); + COM_execute( + cj->re, &scene->r, scene, ntree, srv->name, nullptr, &cj->profiler, cj->needed_outputs); } } @@ -378,8 +381,63 @@ static bool is_compositing_possible(const bContext *C) return true; } +/* Returns the compositor outputs that need to be computed because their result is visible to the + * user. */ +static blender::compositor::OutputTypes get_compositor_needed_outputs(const bContext *C) +{ + blender::compositor::OutputTypes needed_outputs = blender::compositor::OutputTypes::None; + + wmWindowManager *window_manager = CTX_wm_manager(C); + LISTBASE_FOREACH (wmWindow *, window, &window_manager->windows) { + bScreen *screen = WM_window_get_active_screen(window); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + SpaceLink *space_link = static_cast(area->spacedata.first); + if (!space_link || !ELEM(space_link->spacetype, SPACE_NODE, SPACE_IMAGE)) { + continue; + } + if (space_link->spacetype == SPACE_NODE) { + const SpaceNode *space_node = reinterpret_cast(space_link); + if (space_node->flag & SNODE_BACKDRAW) { + needed_outputs |= blender::compositor::OutputTypes::Viewer; + } + if (space_node->overlay.flag & SN_OVERLAY_SHOW_PREVIEWS) { + needed_outputs |= blender::compositor::OutputTypes::Previews; + } + } + else if (space_link->spacetype == SPACE_IMAGE) { + const SpaceImage *space_image = reinterpret_cast(space_link); + Image *image = ED_space_image(space_image); + if (!image || image->source != IMA_SRC_VIEWER) { + continue; + } + if (image->type == IMA_TYPE_R_RESULT) { + needed_outputs |= blender::compositor::OutputTypes::Composite; + } + else if (image->type == IMA_TYPE_COMPOSITE) { + needed_outputs |= blender::compositor::OutputTypes::Viewer; + } + } + + /* All outputs are already needed, return early. */ + if (needed_outputs == + (blender::compositor::OutputTypes::Composite | blender::compositor::OutputTypes::Viewer | + blender::compositor::OutputTypes::Previews)) + { + return needed_outputs; + } + } + } + + return needed_outputs; +} + void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_owner) { + blender::compositor::OutputTypes needed_outputs = get_compositor_needed_outputs(C); + if (needed_outputs == blender::compositor::OutputTypes::None) { + return; + } + using namespace blender::ed::space_node; Main *bmain = CTX_data_main(C); @@ -416,6 +474,7 @@ void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_ cj->view_layer = view_layer; cj->ntree = nodetree; cj->recalc_flags = compo_get_recalc_flags(C); + cj->needed_outputs = needed_outputs; /* Set up job. */ WM_jobs_customdata_set(wm_job, cj, compo_freejob); diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 5a6fd48b355..7a033149c88 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -669,28 +669,6 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params) } } -/* Returns true if an image editor exists that views the compositor result. */ -static bool is_compositor_viewer_image_visible(const bContext *C) -{ - wmWindowManager *window_manager = CTX_wm_manager(C); - LISTBASE_FOREACH (wmWindow *, window, &window_manager->windows) { - bScreen *screen = WM_window_get_active_screen(window); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - SpaceLink *space_link = static_cast(area->spacedata.first); - if (!space_link || space_link->spacetype != SPACE_IMAGE) { - continue; - } - const SpaceImage *space_image = reinterpret_cast(space_link); - Image *image = ED_space_image(space_image); - if (image && image->source == IMA_SRC_VIEWER) { - return true; - } - } - } - - return false; -} - static void node_area_refresh(const bContext *C, ScrArea *area) { /* default now: refresh node is starting preview */ @@ -704,11 +682,7 @@ static void node_area_refresh(const bContext *C, ScrArea *area) if (scene->use_nodes) { if (snode->runtime->recalc_regular_compositing) { snode->runtime->recalc_regular_compositing = false; - /* Only start compositing if its result will be visible either in the backdrop or in a - * viewer image. */ - if (snode->flag & SNODE_BACKDRAW || is_compositor_viewer_image_visible(C)) { - ED_node_composite_job(C, snode->nodetree, scene); - } + ED_node_composite_job(C, snode->nodetree, scene); } } } diff --git a/source/blender/render/RE_compositor.hh b/source/blender/render/RE_compositor.hh index 7adf5e5bd5b..e8d8f6de055 100644 --- a/source/blender/render/RE_compositor.hh +++ b/source/blender/render/RE_compositor.hh @@ -4,9 +4,12 @@ #pragma once +#include + namespace blender::compositor { class RenderContext; class Profiler; +enum class OutputTypes : uint8_t; } // namespace blender::compositor struct bNodeTree; @@ -32,7 +35,8 @@ void RE_compositor_execute(Render &render, const bNodeTree &node_tree, const char *view_name, blender::compositor::RenderContext *render_context, - blender::compositor::Profiler *profiler); + blender::compositor::Profiler *profiler, + blender::compositor::OutputTypes needed_outputs); /* Free compositor caches. */ void RE_compositor_free(Render &render); diff --git a/source/blender/render/intern/compositor.cc b/source/blender/render/intern/compositor.cc index 8fa976b64ed..91f2acd18ef 100644 --- a/source/blender/render/intern/compositor.cc +++ b/source/blender/render/intern/compositor.cc @@ -129,19 +129,22 @@ class ContextInputData { std::string view_name; compositor::RenderContext *render_context; compositor::Profiler *profiler; + compositor::OutputTypes needed_outputs; ContextInputData(const Scene &scene, const RenderData &render_data, const bNodeTree &node_tree, const char *view_name, compositor::RenderContext *render_context, - compositor::Profiler *profiler) + compositor::Profiler *profiler, + compositor::OutputTypes needed_outputs) : scene(&scene), render_data(&render_data), node_tree(&node_tree), view_name(view_name), render_context(render_context), - profiler(profiler) + profiler(profiler), + needed_outputs(needed_outputs) { } }; @@ -217,14 +220,9 @@ class Context : public compositor::Context { this->get_render_data().compositor_denoise_preview_quality); } - bool use_file_output() const override + compositor::OutputTypes needed_outputs() const override { - return this->render_context() != nullptr; - } - - bool should_compute_node_previews() const override - { - return this->render_context() == nullptr; + return input_data_.needed_outputs; } const RenderData &get_render_data() const override @@ -745,12 +743,13 @@ void Render::compositor_execute(const Scene &scene, const bNodeTree &node_tree, const char *view_name, blender::compositor::RenderContext *render_context, - blender::compositor::Profiler *profiler) + blender::compositor::Profiler *profiler, + blender::compositor::OutputTypes needed_outputs) { std::unique_lock lock(this->compositor_mutex); blender::render::ContextInputData input_data( - scene, render_data, node_tree, view_name, render_context, profiler); + scene, render_data, node_tree, view_name, render_context, profiler, needed_outputs); if (this->compositor) { this->compositor->update_input_data(input_data); @@ -785,9 +784,11 @@ void RE_compositor_execute(Render &render, const bNodeTree &node_tree, const char *view_name, blender::compositor::RenderContext *render_context, - blender::compositor::Profiler *profiler) + blender::compositor::Profiler *profiler, + blender::compositor::OutputTypes needed_outputs) { - render.compositor_execute(scene, render_data, node_tree, view_name, render_context, profiler); + render.compositor_execute( + scene, render_data, node_tree, view_name, render_context, profiler, needed_outputs); } void RE_compositor_free(Render &render) diff --git a/source/blender/render/intern/pipeline.cc b/source/blender/render/intern/pipeline.cc index 54a572515f2..e48c22eb07c 100644 --- a/source/blender/render/intern/pipeline.cc +++ b/source/blender/render/intern/pipeline.cc @@ -63,6 +63,7 @@ #include "NOD_composite.hh" #include "COM_compositor.hh" +#include "COM_context.hh" #include "COM_render_context.hh" #include "DEG_depsgraph.hh" @@ -1375,7 +1376,9 @@ static void do_render_compositor(Render *re) ntree, rv->name, &compositor_render_context, - nullptr); + nullptr, + blender::compositor::OutputTypes::Composite | + blender::compositor::OutputTypes::FileOutput); } compositor_render_context.save_file_outputs(re->pipeline_scene_eval); diff --git a/source/blender/render/intern/render_types.h b/source/blender/render/intern/render_types.h index bc847b5a106..3de5a036f4f 100644 --- a/source/blender/render/intern/render_types.h +++ b/source/blender/render/intern/render_types.h @@ -26,6 +26,7 @@ namespace blender::compositor { class RenderContext; class Profiler; +enum class OutputTypes : uint8_t; } // namespace blender::compositor struct bNodeTree; @@ -50,7 +51,8 @@ struct BaseRender { const bNodeTree &node_tree, const char *view_name, blender::compositor::RenderContext *render_context, - blender::compositor::Profiler *profiler) = 0; + blender::compositor::Profiler *profiler, + blender::compositor::OutputTypes needed_outputs) = 0; virtual void compositor_free() = 0; virtual void display_init(RenderResult *render_result) = 0; @@ -102,7 +104,8 @@ struct ViewRender : public BaseRender { const bNodeTree & /*node_tree*/, const char * /*view_name*/, blender::compositor::RenderContext * /*render_context*/, - blender::compositor::Profiler * /*profiler*/) override + blender::compositor::Profiler * /*profiler*/, + blender::compositor::OutputTypes /*needed_outputs*/) override { } void compositor_free() override {} @@ -147,7 +150,8 @@ struct Render : public BaseRender { const bNodeTree &node_tree, const char *view_name, blender::compositor::RenderContext *render_context, - blender::compositor::Profiler *profiler) override; + blender::compositor::Profiler *profiler, + blender::compositor::OutputTypes needed_outputs) override; void compositor_free() override; void display_init(RenderResult *render_result) override;