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;