From 73fcbaf7c4c8c897d3ef5e76069fc7ace6bee11b Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Fri, 26 Sep 2025 08:40:42 +0200 Subject: [PATCH] VSE: Add Compositor modifier This patch adds a new Compositor modifier that applies a compositing node group on a sequencer strip. This patch also introduces the concept of a compositor node tree space subtype, where we now have a Scene and a Sequencer subtypes. Practically, this just means that node like the Render Layers node will not be available in the node editor in the Sequencer subtype. Future improvements includes: - The compositor context is recreated on every modifier application, while it should ideally be persistent somehow to make use of the compositor static cache. This might require work from the compositor side by moving the static cache outside of the context and make it thread safe if needed. See `Render.compositor` for an example on persistent context. - GPU execution is not supported. This just needs a GPU context to be bound before execution, but the tricky part is getting a GPU context. See `render::Compositor::execute` for an example on bounding a GPU context and why it is less straight forward. - Node inputs are not exposed on the sequencer modifier interface. An approach similar to Geometry Nodes modifier could be used, look at `update_input_properties_from_node_tree` for reference, but notice that Geometry Nodes naturally exempt the main Geometry socket because Geometry inputs can't be exposed, but for the compositor, we will have to exempt the main Color and Mask sockets manually. !145971 Co-authored-by: Aras Pranckevicius Co-authored-by: Falk David Pull Request: https://projects.blender.org/blender/blender/pulls/139634 --- .../startup/bl_ui/node_add_menu_compositor.py | 9 +- scripts/startup/bl_ui/space_node.py | 28 +- scripts/startup/bl_ui/space_sequencer.py | 1 + source/blender/blenkernel/intern/scene.cc | 4 + .../intern/builder/deg_builder_nodes.cc | 12 + .../intern/builder/deg_builder_relations.cc | 15 ++ source/blender/editors/include/ED_node_c.hh | 2 +- .../blender/editors/render/render_update.cc | 8 + .../blender/editors/space_node/CMakeLists.txt | 2 + source/blender/editors/space_node/drawnode.cc | 4 +- source/blender/editors/space_node/node_add.cc | 86 +++++++ .../editors/space_node/node_context_path.cc | 48 +++- .../blender/editors/space_node/node_edit.cc | 2 +- .../blender/editors/space_node/node_intern.hh | 1 + source/blender/editors/space_node/node_ops.cc | 1 + source/blender/makesdna/DNA_sequence_types.h | 6 + source/blender/makesdna/DNA_space_enums.h | 6 + .../blender/makesrna/intern/rna_sequencer.cc | 49 ++++ source/blender/makesrna/intern/rna_space.cc | 75 ++++-- source/blender/nodes/NOD_composite.hh | 1 + source/blender/nodes/composite/CMakeLists.txt | 2 + .../nodes/composite/node_composite_tree.cc | 40 +++ .../nodes/node_composite_file_output.cc | 14 + .../nodes/node_composite_group_input.cc | 63 ++++- .../composite/nodes/node_composite_image.cc | 12 +- .../nodes/intern/geometry_nodes_log.cc | 4 + source/blender/nodes/intern/node_common.cc | 6 + source/blender/sequencer/CMakeLists.txt | 6 + source/blender/sequencer/SEQ_modifier.hh | 6 +- source/blender/sequencer/SEQ_modifiertypes.hh | 1 + source/blender/sequencer/SEQ_relations.hh | 6 + source/blender/sequencer/SEQ_sequencer.hh | 10 + .../modifiers/MOD_brightness_contrast.cc | 3 +- .../intern/modifiers/MOD_color_balance.cc | 3 +- .../intern/modifiers/MOD_compositor.cc | 242 ++++++++++++++++++ .../sequencer/intern/modifiers/MOD_curves.cc | 3 +- .../intern/modifiers/MOD_hue_correct.cc | 3 +- .../sequencer/intern/modifiers/MOD_mask.cc | 3 +- .../sequencer/intern/modifiers/MOD_tonemap.cc | 3 +- .../intern/modifiers/MOD_white_balance.cc | 3 +- .../sequencer/intern/modifiers/modifier.cc | 3 +- .../blender/sequencer/intern/strip_lookup.cc | 34 +++ .../sequencer/intern/strip_relations.cc | 11 + 43 files changed, 793 insertions(+), 48 deletions(-) create mode 100644 source/blender/sequencer/intern/modifiers/MOD_compositor.cc diff --git a/scripts/startup/bl_ui/node_add_menu_compositor.py b/scripts/startup/bl_ui/node_add_menu_compositor.py index 9c5e1550182..65653d2f41f 100644 --- a/scripts/startup/bl_ui/node_add_menu_compositor.py +++ b/scripts/startup/bl_ui/node_add_menu_compositor.py @@ -49,7 +49,8 @@ class NODE_MT_compositor_node_input_scene_base(node_add_menu.NodeMenu): def draw(self, context): layout = self.layout - self.node_operator(layout, "CompositorNodeRLayers") + if context.space_data.node_tree_sub_type == 'SCENE': + self.node_operator(layout, "CompositorNodeRLayers") self.node_operator_with_outputs(context, layout, "CompositorNodeSceneTime", ["Frame", "Seconds"]) self.node_operator(layout, "CompositorNodeTime") @@ -60,12 +61,12 @@ class NODE_MT_compositor_node_output_base(node_add_menu.NodeMenu): bl_label = "Output" def draw(self, context): - del context layout = self.layout self.node_operator(layout, "NodeGroupOutput") self.node_operator(layout, "CompositorNodeViewer") - layout.separator() - self.node_operator(layout, "CompositorNodeOutputFile") + if context.space_data.node_tree_sub_type == 'SCENE': + layout.separator() + self.node_operator(layout, "CompositorNodeOutputFile") self.draw_assets_for_catalog(layout, self.bl_label) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 69e107bba72..b028b0fe437 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -145,13 +145,29 @@ class NODE_HT_header(Header): layout.template_ID(id_from, "active_texture", new="texture.new") elif snode.tree_type == 'CompositorNodeTree': - + layout.prop(snode, "node_tree_sub_type", text="") NODE_MT_editor_menus.draw_collapsible(context, layout) - layout.separator_spacer() - row = layout.row() - row.enabled = not snode.pin - row.template_ID(scene, "compositing_node_group", new="node.new_compositing_node_group") + + if snode.node_tree_sub_type == 'SCENE': + row = layout.row() + row.enabled = not snode.pin + row.template_ID(scene, "compositing_node_group", new="node.new_compositing_node_group") + elif snode.node_tree_sub_type == 'SEQUENCER': + row = layout.row() + sequencer_scene = context.workspace.sequencer_scene + sequencer_editor = sequencer_scene.sequence_editor if sequencer_scene else None + active_strip = sequencer_editor.active_strip if sequencer_editor else None + active_modifier = active_strip.modifiers.active if active_strip else None + is_compositor_modifier_active = active_modifier and active_modifier.type == 'COMPOSITOR' + if is_compositor_modifier_active and not snode.pin: + row.template_ID( + active_modifier, + "node_group", + new="node.new_compositor_sequencer_node_group") + else: + row.enabled = False + row.template_ID(snode, "node_tree", new="node.new_compositor_sequencer_node_group") elif snode.tree_type == 'GeometryNodeTree': layout.prop(snode, "node_tree_sub_type", text="") @@ -200,7 +216,7 @@ class NODE_HT_header(Header): op.parent_tree_index = len(snode.path) - 2 # Backdrop - if is_compositor: + if is_compositor and snode.node_tree_sub_type == 'SCENE': row = layout.row(align=True) row.prop(snode, "show_backdrop", toggle=True) row.active = snode.node_tree is not None diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index 953e488dce3..4faf2bb551b 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -1571,6 +1571,7 @@ class SEQUENCER_MT_modifier_add(Menu): else: self.operator_modifier_add(layout, 'BRIGHT_CONTRAST') self.operator_modifier_add(layout, 'COLOR_BALANCE') + self.operator_modifier_add(layout, 'COMPOSITOR') self.operator_modifier_add(layout, 'CURVES') self.operator_modifier_add(layout, 'HUE_CORRECT') self.operator_modifier_add(layout, 'MASK') diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 3d1578418f1..27c98113d0c 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -807,6 +807,10 @@ static bool strip_foreach_member_id_cb(Strip *strip, void *user_data) }); LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) { FOREACHID_PROCESS_IDSUPER(data, smd->mask_id, IDWALK_CB_USER); + if (smd->type == eSeqModifierType_Compositor) { + auto *modifier_data = reinterpret_cast(smd); + FOREACHID_PROCESS_IDSUPER(data, modifier_data->node_group, IDWALK_CB_USER); + } } if (strip->type == STRIP_TYPE_TEXT && strip->effectdata) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index d37de16e23e..9d5e36ad191 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -2322,6 +2322,18 @@ static bool strip_node_build_cb(Strip *strip, void *user_data) ViewLayer *sequence_view_layer = BKE_view_layer_default_render(strip->scene); nb->build_scene_speakers(strip->scene, sequence_view_layer); } + LISTBASE_FOREACH (StripModifierData *, modifier, &strip->modifiers) { + if (modifier->type != eSeqModifierType_Compositor) { + continue; + } + + const SequencerCompositorModifierData *modifier_data = + reinterpret_cast(modifier); + if (!modifier_data->node_group) { + continue; + } + nb->build_nodetree(modifier_data->node_group); + } /* TODO(sergey): Movie clip, scene, camera, mask. */ return true; } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 7a8ed6f253e..c1a97036cdf 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -3441,6 +3441,21 @@ static bool strip_build_prop_cb(Strip *strip, void *user_data) ViewLayer *sequence_view_layer = BKE_view_layer_default_render(strip->scene); cd->builder->build_scene_speakers(strip->scene, sequence_view_layer); } + LISTBASE_FOREACH (StripModifierData *, modifier, &strip->modifiers) { + if (modifier->type != eSeqModifierType_Compositor) { + continue; + } + + const SequencerCompositorModifierData *modifier_data = + reinterpret_cast(modifier); + if (!modifier_data->node_group) { + continue; + } + cd->builder->build_nodetree(modifier_data->node_group); + OperationKey node_tree_key( + &modifier_data->node_group->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); + cd->builder->add_relation(node_tree_key, cd->sequencer_key, "Modifier's Node Group"); + } /* TODO(sergey): Movie clip, camera, mask. */ return true; } diff --git a/source/blender/editors/include/ED_node_c.hh b/source/blender/editors/include/ED_node_c.hh index 9686e93f449..4cda54f8de0 100644 --- a/source/blender/editors/include/ED_node_c.hh +++ b/source/blender/editors/include/ED_node_c.hh @@ -71,7 +71,7 @@ void ED_node_set_tree_type(SpaceNode *snode, blender::bke::bNodeTreeType *typein bool ED_node_is_compositor(const SpaceNode *snode); bool ED_node_is_shader(SpaceNode *snode); bool ED_node_is_texture(SpaceNode *snode); -bool ED_node_is_geometry(SpaceNode *snode); +bool ED_node_is_geometry(const SpaceNode *snode); bool ED_node_supports_preview(SpaceNode *snode); /** diff --git a/source/blender/editors/render/render_update.cc b/source/blender/editors/render/render_update.cc index cc852292d81..6965c351fdf 100644 --- a/source/blender/editors/render/render_update.cc +++ b/source/blender/editors/render/render_update.cc @@ -328,6 +328,14 @@ static void update_sequencer(const DEGEditorUpdateContext *update_ctx, Main *bma blender::seq::cache_cleanup(changed_scene); } } + + /* Invalidate cache for strips that use this compositing tree as a modifier. */ + if (GS(id->name) == ID_NT) { + const bNodeTree *node_tree = reinterpret_cast(id); + if (node_tree->type == NTREE_COMPOSIT) { + blender::seq::relations_invalidate_compositor_modifiers(bmain, node_tree); + } + } } void ED_render_id_flush_update(const DEGEditorUpdateContext *update_ctx, ID *id) diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 8567669bc7a..519275b6a5f 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -12,6 +12,7 @@ set(INC ../../compositor/utilities ../../makesrna ../../nodes/geometry/include + ../../sequencer ../asset # RNA_prototypes.hh @@ -69,6 +70,7 @@ set(LIB PRIVATE bf::nodes PRIVATE bf::render PRIVATE bf::windowmanager + PRIVATE bf::sequencer ) if(WITH_OPENIMAGEDENOISE) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index f67d1c5984b..49a60faae52 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1494,7 +1494,9 @@ void draw_nodespace_back_pix(const bContext &C, GPU_matrix_pop_projection(); GPU_matrix_pop(); - if (!(snode.flag & SNODE_BACKDRAW) || !ED_node_is_compositor(&snode)) { + if (!(snode.flag & SNODE_BACKDRAW) || !ED_node_is_compositor(&snode) || + snode.node_tree_sub_type != SNODE_COMPOSITOR_SCENE) + { return; } diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index fca972e7e95..094a9212f76 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -1733,6 +1733,92 @@ void NODE_OT_new_compositing_node_group(wmOperatorType *ot) RNA_def_string(ot->srna, "name", DATA_("Compositor Nodes"), MAX_ID_NAME - 2, "Name", ""); } +/* -------------------------------------------------------------------- */ +/** \name New Compositor Sequencer Node Group Operator + * \{ */ + +static void initialize_compositor_sequencer_node_group(const bContext *C, bNodeTree &ntree) +{ + BLI_assert(ntree.type == NTREE_COMPOSIT); + BLI_assert(BLI_listbase_count(&ntree.nodes) == 0); + + ntree.tree_interface.add_socket( + DATA_("Image"), "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr); + ntree.tree_interface.add_socket( + DATA_("Mask"), "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr); + ntree.tree_interface.add_socket( + DATA_("Image"), "", "NodeSocketColor", NODE_INTERFACE_SOCKET_OUTPUT, nullptr); + + bNode *output_node = blender::bke::node_add_node(C, ntree, "NodeGroupOutput"); + output_node->location[0] = 200.0f; + output_node->location[1] = 0.0f; + + bNode *input_node = blender::bke::node_add_node(C, ntree, "NodeGroupInput"); + input_node->location[0] = -150.0f - input_node->width; + input_node->location[1] = 0.0f; + blender::bke::node_set_active(ntree, *input_node); + + bNode *reroute = blender::bke::node_add_static_node(C, ntree, NODE_REROUTE); + reroute->location[0] = 100.0f; + reroute->location[1] = -35.0f; + + bNode *viewer = blender::bke::node_add_static_node(C, ntree, CMP_NODE_VIEWER); + viewer->location[0] = 200.0f; + viewer->location[1] = -80.0f; + + blender::bke::node_add_link(ntree, + *input_node, + *static_cast(input_node->outputs.first), + *reroute, + *static_cast(reroute->inputs.first)); + + blender::bke::node_add_link(ntree, + *reroute, + *static_cast(reroute->outputs.first), + *output_node, + *static_cast(output_node->inputs.first)); + + blender::bke::node_add_link(ntree, + *reroute, + *static_cast(reroute->outputs.first), + *viewer, + *static_cast(viewer->inputs.first)); + + BKE_ntree_update_after_single_tree_change(*CTX_data_main(C), ntree); +} + +static wmOperatorStatus new_compositor_sequencer_node_group_exec(bContext *C, wmOperator *op) +{ + char tree_name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", tree_name); + + bNodeTree *ntree = new_node_tree_impl(C, tree_name, "CompositorNodeTree"); + initialize_compositor_sequencer_node_group(C, *ntree); + + BKE_ntree_update_after_single_tree_change(*CTX_data_main(C), *ntree); + WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_new_compositor_sequencer_node_group(wmOperatorType *operator_type) +{ + operator_type->name = "New Compositor Sequencer Node Group"; + operator_type->idname = "NODE_OT_new_compositor_sequencer_node_group"; + operator_type->description = "Create a new compositor node group for sequencer"; + + operator_type->exec = new_compositor_sequencer_node_group_exec; + + operator_type->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_string(operator_type->srna, + "name", + DATA_("Sequencer Compositor Nodes"), + MAX_ID_NAME - 2, + "Name", + ""); +} + /** \} */ } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index 1dbfa84d09b..66df55ad924 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -21,6 +21,10 @@ #include "ED_screen.hh" +#include "SEQ_modifier.hh" +#include "SEQ_select.hh" +#include "SEQ_sequencer.hh" + #include "WM_api.hh" #include "UI_interface.hh" @@ -162,9 +166,47 @@ static void get_context_path_node_compositor(const bContext &C, context_path_add_node_tree_and_node_groups(snode, path); } else { - Scene *scene = CTX_data_scene(&C); - ui::context_path_add_generic(path, RNA_Scene, scene); - context_path_add_node_tree_and_node_groups(snode, path); + if (snode.node_tree_sub_type == SNODE_COMPOSITOR_SEQUENCER) { + Scene *sequencer_scene = CTX_data_sequencer_scene(&C); + if (!sequencer_scene) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + Editing *ed = seq::editing_get(sequencer_scene); + if (!ed) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + Strip *strip = seq::select_active_get(sequencer_scene); + if (!strip) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + StripModifierData *smd = seq::modifier_get_active(strip); + if (!smd) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + if (smd->type != eSeqModifierType_Compositor) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + SequencerCompositorModifierData *scmd = reinterpret_cast( + smd); + if (scmd->node_group == nullptr) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + ui::context_path_add_generic(path, RNA_Scene, sequencer_scene, ICON_SCENE); + ui::context_path_add_generic(path, RNA_Strip, strip, ICON_SEQ_STRIP_DUPLICATE); + ui::context_path_add_generic(path, RNA_NodeTree, scmd->node_group); + context_path_add_node_tree_and_node_groups(snode, path, true); + } + else { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + context_path_add_node_tree_and_node_groups(snode, path); + } } } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index a71ae180da3..af9f082cd50 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -504,7 +504,7 @@ bool ED_node_is_texture(SpaceNode *snode) return snode->tree_idname == ntreeType_Texture->idname; } -bool ED_node_is_geometry(SpaceNode *snode) +bool ED_node_is_geometry(const SpaceNode *snode) { return snode->tree_idname == ntreeType_Geometry->idname; } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 07e26ec4d4e..a13d2d0c664 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -310,6 +310,7 @@ void NODE_OT_add_import_node(wmOperatorType *ot); void NODE_OT_swap_group_asset(wmOperatorType *ot); void NODE_OT_new_node_tree(wmOperatorType *ot); void NODE_OT_new_compositing_node_group(wmOperatorType *ot); +void NODE_OT_new_compositor_sequencer_node_group(wmOperatorType *operator_type); void NODE_OT_add_group_input_node(wmOperatorType *ot); /* `node_group.cc` */ diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index 94e5fad6607..7c27b98fa57 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -95,6 +95,7 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_new_node_tree); WM_operatortype_append(NODE_OT_new_compositing_node_group); + WM_operatortype_append(NODE_OT_new_compositor_sequencer_node_group); WM_operatortype_append(NODE_OT_parent_set); WM_operatortype_append(NODE_OT_join); diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index c6d492b118f..35a697d9779 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -625,6 +625,11 @@ typedef enum eModTonemapType { SEQ_TONEMAP_RD_PHOTORECEPTOR = 1, } eModTonemapType; +typedef struct SequencerCompositorModifierData { + StripModifierData modifier; + struct bNodeTree *node_group; +} SequencerCompositorModifierData; + /** \} */ /* -------------------------------------------------------------------- */ @@ -888,6 +893,7 @@ typedef enum eStripModifierType { eSeqModifierType_WhiteBalance = 6, eSeqModifierType_Tonemap = 7, eSeqModifierType_SoundEqualizer = 8, + eSeqModifierType_Compositor = 9, /* Keep last. */ NUM_STRIP_MODIFIER_TYPES, } eStripModifierType; diff --git a/source/blender/makesdna/DNA_space_enums.h b/source/blender/makesdna/DNA_space_enums.h index 9c7d2d4044e..77bbe6455a9 100644 --- a/source/blender/makesdna/DNA_space_enums.h +++ b/source/blender/makesdna/DNA_space_enums.h @@ -879,6 +879,12 @@ typedef enum SpaceNodeGeometryNodesType { SNODE_GEOMETRY_TOOL = 1, } SpaceNodeGeometryNodesType; +/** #SpaceNode.nodes_type */ +typedef enum SpaceNodeCompositorNodesType { + SNODE_COMPOSITOR_SCENE = 0, + SNODE_COMPOSITOR_SEQUENCER = 1, +} SpaceNodeCompositorNodesType; + /** #SpaceNode.insert_ofs_dir */ enum { SNODE_INSERTOFS_DIR_RIGHT = 0, diff --git a/source/blender/makesrna/intern/rna_sequencer.cc b/source/blender/makesrna/intern/rna_sequencer.cc index 831d6ca383d..c73585db8ca 100644 --- a/source/blender/makesrna/intern/rna_sequencer.cc +++ b/source/blender/makesrna/intern/rna_sequencer.cc @@ -44,6 +44,7 @@ struct EffectInfo { #define RNA_ENUM_SEQUENCER_VIDEO_MODIFIER_TYPE_ITEMS \ {eSeqModifierType_BrightContrast, "BRIGHT_CONTRAST", ICON_MOD_BRIGHTNESS_CONTRAST, "Brightness/Contrast", ""}, \ {eSeqModifierType_ColorBalance, "COLOR_BALANCE", ICON_MOD_COLOR_BALANCE, "Color Balance", ""}, \ + {eSeqModifierType_Compositor, "COMPOSITOR", ICON_NODE_COMPOSITING, "Compositor", ""}, \ {eSeqModifierType_Curves, "CURVES", ICON_MOD_CURVES, "Curves", ""}, \ {eSeqModifierType_HueCorrect, "HUE_CORRECT", ICON_MOD_HUE_CORRECT, "Hue Correct", ""}, \ {eSeqModifierType_Mask, "MASK", ICON_MOD_MASK, "Mask", ""}, \ @@ -1444,6 +1445,8 @@ static StructRNA *rna_StripModifier_refine(PointerRNA *ptr) return &RNA_SequencerTonemapModifierData; case eSeqModifierType_SoundEqualizer: return &RNA_SoundEqualizerModifier; + case eSeqModifierType_Compositor: + return &RNA_SequencerCompositorModifierData; default: return &RNA_StripModifier; } @@ -1742,6 +1745,33 @@ static void rna_Strip_SoundEqualizer_Curve_clear(SoundEqualizerModifierData *sem WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); } +static bool rna_CompositorModifier_node_group_poll(PointerRNA * /*ptr*/, PointerRNA value) +{ + const bNodeTree *node_tree = value.data_as(); + if (node_tree->type != NTREE_COMPOSIT) { + return false; + } + return true; +} + +static void rna_CompositorModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_StripModifier_update(bmain, scene, ptr); + + /* Tag depsgraph relations for an update since the modifier could now be referencing a different + * node tree. */ + DEG_relations_tag_update(bmain); + + /* Strips from other scenes could be modified, so use the scene of the strip as opposed to the + * active scene argument. */ + Scene *strip_scene = reinterpret_cast(ptr->owner_id); + Editing *ed = blender::seq::editing_get(strip_scene); + + /* The sequencer stores a cached mapping between compositor node trees and strips that use them + * as a modifier, so we need to invalidate the cache since the node tree changed. */ + blender::seq::strip_lookup_invalidate(ed); +} + #else static void rna_def_strip_element(BlenderRNA *brna) @@ -4065,6 +4095,24 @@ static void rna_def_tonemap_modifier(BlenderRNA *brna) rna_def_modifier_panel_open_prop(srna, "open_mask_input_panel", 1); } +static void rna_def_compositor_modifier(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "SequencerCompositorModifierData", "StripModifier"); + RNA_def_struct_sdna(srna, "SequencerCompositorModifierData"); + RNA_def_struct_ui_icon(srna, ICON_NODE_COMPOSITING); + RNA_def_struct_ui_text(srna, "SequencerCompositorModifierData", "Compositor Modifier"); + + PropertyRNA *prop = RNA_def_property(srna, "node_group", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Node Group", "Node group that controls what this modifier does"); + RNA_def_property_pointer_funcs( + prop, nullptr, nullptr, nullptr, "rna_CompositorModifier_node_group_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_update( + prop, NC_SCENE | ND_SEQUENCER, "rna_CompositorModifier_node_group_update"); + + rna_def_modifier_panel_open_prop(srna, "open_mask_input_panel", 1); +} + static void rna_def_modifiers(BlenderRNA *brna) { rna_def_modifier(brna); @@ -4076,6 +4124,7 @@ static void rna_def_modifiers(BlenderRNA *brna) rna_def_brightcontrast_modifier(brna); rna_def_whitebalance_modifier(brna); rna_def_tonemap_modifier(brna); + rna_def_compositor_modifier(brna); } static void rna_def_graphical_sound_equalizer(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index ec737a08080..a9561bb9f02 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -2553,10 +2553,15 @@ static void rna_SpaceNodeEditor_node_tree_set(PointerRNA *ptr, ED_node_tree_start(region, snode, (bNodeTree *)value.data, nullptr, nullptr); } -static bool rna_SpaceNodeEditor_selected_node_group_poll(PointerRNA * /*ptr*/, +static bool rna_SpaceNodeEditor_selected_node_group_poll(PointerRNA *space_node_pointer, const PointerRNA value) { + SpaceNode *space_node = space_node_pointer->data_as(); const bNodeTree &ntree = *static_cast(value.data); + if (ED_node_is_compositor(space_node)) { + return ntree.type == NTREE_COMPOSIT; + } + if (ntree.type != NTREE_GEOMETRY) { return false; } @@ -2614,13 +2619,55 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA * blender::ed::space_node::tree_update(C); } +static const EnumPropertyItem *rna_SpaceNodeEditor_node_tree_sub_type_itemf( + bContext * /*context*/, + PointerRNA *space_node_pointer, + PropertyRNA * /*property*/, + bool * /*r_free*/) +{ + static const EnumPropertyItem geometry_nodes_sub_type_items[] = { + {SNODE_GEOMETRY_MODIFIER, + "MODIFIER", + ICON_MODIFIER_DATA, + "Modifier", + "Edit node group from active object's active modifier"}, + {SNODE_GEOMETRY_TOOL, + "TOOL", + ICON_TOOL_SETTINGS, + "Tool", + "Edit any geometry node group for use as an operator"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem compositor_sub_type_items[] = { + {SNODE_COMPOSITOR_SCENE, "SCENE", 0, "Scene", "Edit node group for the current scene"}, + {SNODE_COMPOSITOR_SEQUENCER, "SEQUENCER", 0, "Sequencer", "Edit node group for sequencer"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + SpaceNode *space_node = space_node_pointer->data_as(); + if (ED_node_is_geometry(space_node)) { + return geometry_nodes_sub_type_items; + } + else { + return compositor_sub_type_items; + } +} + static void rna_SpaceNodeEditor_node_tree_sub_type_update(Main * /*main*/, Scene * /*scene*/, - PointerRNA *ptr) + PointerRNA *space_node_pointer) { - SpaceNode *snode = static_cast(ptr->data); - if (snode->node_tree_sub_type == SNODE_GEOMETRY_TOOL) { - snode->flag &= ~SNODE_PIN; + SpaceNode *space_node = space_node_pointer->data_as(); + if (ED_node_is_geometry(space_node)) { + if (space_node->node_tree_sub_type == SNODE_GEOMETRY_TOOL) { + space_node->flag &= ~SNODE_PIN; + } + } + else { + if (space_node->node_tree_sub_type == SNODE_COMPOSITOR_SEQUENCER) { + space_node->flag &= ~SNODE_PIN; + } } } @@ -7899,20 +7946,6 @@ static void rna_def_space_node(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; - static const EnumPropertyItem geometry_nodes_type_items[] = { - {SNODE_GEOMETRY_MODIFIER, - "MODIFIER", - ICON_MODIFIER_DATA, - "Modifier", - "Edit node group from active object's active modifier"}, - {SNODE_GEOMETRY_TOOL, - "TOOL", - ICON_TOOL_SETTINGS, - "Tool", - "Edit any geometry node group for use as an operator"}, - {0, nullptr, 0, nullptr, nullptr}, - }; - static const EnumPropertyItem backdrop_channels_items[] = { {SNODE_USE_ALPHA, "COLOR_ALPHA", @@ -7966,7 +7999,9 @@ static void rna_def_space_node(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree_sub_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, nullptr, "node_tree_sub_type"); - RNA_def_property_enum_items(prop, geometry_nodes_type_items); + RNA_def_property_enum_items(prop, rna_enum_dummy_NULL_items); + RNA_def_property_enum_funcs( + prop, nullptr, nullptr, "rna_SpaceNodeEditor_node_tree_sub_type_itemf"); RNA_def_property_ui_text(prop, "Node Tree Sub-Type", ""); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); RNA_def_property_update( diff --git a/source/blender/nodes/NOD_composite.hh b/source/blender/nodes/NOD_composite.hh index c99a25c106d..654d73e4738 100644 --- a/source/blender/nodes/NOD_composite.hh +++ b/source/blender/nodes/NOD_composite.hh @@ -83,5 +83,6 @@ compositor::NodeOperation *get_group_input_compositor_operation(compositor::Cont compositor::NodeOperation *get_group_output_compositor_operation(compositor::Context &context, DNode node); void get_compositor_group_output_extra_info(blender::nodes::NodeExtraInfoParams ¶meters); +void get_compositor_group_input_extra_info(blender::nodes::NodeExtraInfoParams ¶meters); } // namespace blender::nodes diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index e122754bd09..34b018559ce 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -14,6 +14,7 @@ set(INC ../../compositor/cached_resources ../../compositor/derived_resources ../../compositor/utilities + ../../sequencer # RNA_prototypes.hh ${CMAKE_BINARY_DIR}/source/blender/makesrna @@ -129,6 +130,7 @@ set(LIB PRIVATE bf::render PRIVATE bf::windowmanager PRIVATE bf::dependencies::optional::opencolorio + PRIVATE bf::sequencer ) add_node_discovery( diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index 3c756560d64..c331c3b8059 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -8,6 +8,7 @@ #include "DNA_node_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" @@ -22,6 +23,10 @@ #include "UI_resources.hh" +#include "SEQ_modifier.hh" +#include "SEQ_select.hh" +#include "SEQ_sequencer.hh" + #include "node_common.h" #include "RNA_prototypes.hh" @@ -35,6 +40,41 @@ static void composite_get_from_context(const bContext *C, ID **r_id, ID **r_from) { + using namespace blender; + const SpaceNode *snode = CTX_wm_space_node(C); + if (snode->node_tree_sub_type == SNODE_COMPOSITOR_SEQUENCER) { + Scene *sequencer_scene = CTX_data_sequencer_scene(C); + if (!sequencer_scene) { + *r_ntree = nullptr; + return; + } + Editing *ed = seq::editing_get(sequencer_scene); + if (!ed) { + *r_ntree = nullptr; + return; + } + Strip *strip = seq::select_active_get(sequencer_scene); + if (!strip) { + *r_ntree = nullptr; + return; + } + StripModifierData *smd = seq::modifier_get_active(strip); + if (!smd) { + *r_ntree = nullptr; + return; + } + if (smd->type != eSeqModifierType_Compositor) { + *r_ntree = nullptr; + return; + } + SequencerCompositorModifierData *scmd = reinterpret_cast( + smd); + *r_from = nullptr; + *r_id = &sequencer_scene->id; + *r_ntree = scmd->node_group; + return; + } + Scene *scene = CTX_data_scene(C); *r_from = nullptr; diff --git a/source/blender/nodes/composite/nodes/node_composite_file_output.cc b/source/blender/nodes/composite/nodes/node_composite_file_output.cc index b99386d66cd..d4c3631d2b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_file_output.cc +++ b/source/blender/nodes/composite/nodes/node_composite_file_output.cc @@ -49,6 +49,7 @@ #include "COM_utilities.hh" #include "NOD_compositor_file_output.hh" +#include "NOD_node_extra_info.hh" #include "NOD_socket_items_blend.hh" #include "NOD_socket_items_ops.hh" #include "NOD_socket_items_ui.hh" @@ -376,6 +377,18 @@ static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader & socket_items::blend_read_data(&reader, node); } +static void node_extra_info(NodeExtraInfoParams ¶meters) +{ + SpaceNode *space_node = CTX_wm_space_node(¶meters.C); + if (space_node->node_tree_sub_type != SNODE_COMPOSITOR_SCENE) { + NodeExtraInfoRow row; + row.text = RPT_("Node Unsupported"); + row.tooltip = TIP_("The File Output node is only supported for scene compositing"); + row.icon = ICON_ERROR; + parameters.rows.append(std::move(row)); + } +} + using namespace blender::compositor; class FileOutputOperation : public NodeOperation { @@ -826,6 +839,7 @@ static void node_register() ntype, "NodeCompositorFileOutput", node_free_storage, node_copy_storage); ntype.blend_write_storage_content = node_blend_write; ntype.blend_data_read_storage_content = node_blend_read; + ntype.get_extra_info = node_extra_info; ntype.get_compositor_operation = get_compositor_operation; blender::bke::node_register_type(ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_group_input.cc b/source/blender/nodes/composite/nodes/node_composite_group_input.cc index 5b4f042c3db..a11325168d7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_group_input.cc +++ b/source/blender/nodes/composite/nodes/node_composite_group_input.cc @@ -6,7 +6,16 @@ #include "GPU_shader.hh" -#include "NOD_composite.hh" /* Own include. */ +#include "BLT_translation.hh" + +#include "UI_resources.hh" + +#include "DNA_space_types.h" + +#include "BKE_context.hh" + +#include "NOD_composite.hh" +#include "NOD_node_extra_info.hh" #include "COM_node_operation.hh" #include "COM_utilities.hh" @@ -137,4 +146,56 @@ compositor::NodeOperation *get_group_input_compositor_operation(compositor::Cont return new node_composite_group_input_cc::GroupInputOperation(context, node); } +void get_compositor_group_input_extra_info(blender::nodes::NodeExtraInfoParams ¶meters) +{ + if (parameters.tree.type != NTREE_COMPOSIT) { + return; + } + + SpaceNode *space_node = CTX_wm_space_node(¶meters.C); + if (space_node->edittree != space_node->nodetree) { + return; + } + + if (space_node->node_tree_sub_type != SNODE_COMPOSITOR_SEQUENCER) { + return; + } + + Span group_inputs = parameters.node.output_sockets().drop_back(1); + bool added_warning_for_unsupported_inputs = false; + for (const bNodeSocket *input : group_inputs) { + if (StringRef(input->name) == "Image") { + if (input->type != SOCK_RGBA) { + blender::nodes::NodeExtraInfoRow row; + row.text = IFACE_("Wrong Image Input Type"); + row.icon = ICON_ERROR; + row.tooltip = TIP_("Node group's main Image input should be of type Color"); + parameters.rows.append(std::move(row)); + } + } + else if (StringRef(input->name) == "Mask") { + if (input->type != SOCK_RGBA) { + blender::nodes::NodeExtraInfoRow row; + row.text = IFACE_("Wrong Mask Input Type"); + row.icon = ICON_ERROR; + row.tooltip = TIP_("Node group's Mask input should be of type Color"); + parameters.rows.append(std::move(row)); + } + } + else { + if (added_warning_for_unsupported_inputs) { + continue; + } + blender::nodes::NodeExtraInfoRow row; + row.text = IFACE_("Unsupported Inputs"); + row.icon = ICON_WARNING_LARGE; + row.tooltip = TIP_( + "Only a main Image and Mask inputs are supported, the rest are unsupported and will " + "return zero"); + parameters.rows.append(std::move(row)); + added_warning_for_unsupported_inputs = true; + } + } +} + } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc index 74e57b3f7c7..f424ec97df7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -29,6 +29,7 @@ #include "DNA_image_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "DNA_vec_types.h" #include "RE_engine.h" @@ -749,12 +750,19 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer RNA_string_set(&op_ptr, "scene", scene_name); } -/* Give a warning if passes are used with a render engine that does not support them. */ static void node_extra_info(NodeExtraInfoParams ¶meters) { - const Scene *scene = CTX_data_scene(¶meters.C); + SpaceNode *space_node = CTX_wm_space_node(¶meters.C); + if (space_node->node_tree_sub_type != SNODE_COMPOSITOR_SCENE) { + NodeExtraInfoRow row; + row.text = RPT_("Node Unsupported"); + row.tooltip = TIP_("The Render Layers node is only supported for scene compositing"); + row.icon = ICON_ERROR; + parameters.rows.append(std::move(row)); + } /* EEVEE supports passes. */ + const Scene *scene = CTX_data_scene(¶meters.C); if (StringRef(scene->r.engine) == RE_engine_id_BLENDER_EEVEE) { return; } diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index e5d7621c3b4..f4969ac84c4 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -934,6 +934,10 @@ Map GeoNodesLog:: static GeoNodesLog *get_root_log(const SpaceNode &snode) { + if (!ED_node_is_geometry(&snode)) { + return nullptr; + } + switch (SpaceNodeGeometryNodesType(snode.node_tree_sub_type)) { case SNODE_GEOMETRY_MODIFIER: { std::optional object_and_modifier = diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index cf577c1adb9..d7731e47250 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -869,6 +869,11 @@ static bool group_output_insert_link(blender::bke::NodeInsertLinkParams ¶ms) } // namespace blender::nodes +static void node_group_input_extra_info(blender::nodes::NodeExtraInfoParams ¶ms) +{ + get_compositor_group_input_extra_info(params); +} + void register_node_type_group_input() { /* used for all tree types, needs dynamic allocation */ @@ -884,6 +889,7 @@ void register_node_type_group_input() blender::bke::node_type_size(*ntype, 140, 80, 400); ntype->declare = blender::nodes::group_input_declare; ntype->insert_link = blender::nodes::group_input_insert_link; + ntype->get_extra_info = node_group_input_extra_info; ntype->get_compositor_operation = blender::nodes::get_group_input_compositor_operation; blender::bke::node_register_type(*ntype); diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt index d2af2e43836..bd53ebb4cfd 100644 --- a/source/blender/sequencer/CMakeLists.txt +++ b/source/blender/sequencer/CMakeLists.txt @@ -5,6 +5,9 @@ set(INC PUBLIC . intern + ../compositor + ../compositor/cached_resources + ../compositor/derived_resources ../editors/include ../animrig ../makesrna @@ -68,6 +71,7 @@ set(SRC intern/effects/vse_effect_wipe.cc intern/modifiers/MOD_brightness_contrast.cc intern/modifiers/MOD_color_balance.cc + intern/modifiers/MOD_compositor.cc intern/modifiers/MOD_curves.cc intern/modifiers/MOD_hue_correct.cc intern/modifiers/MOD_mask.cc @@ -120,8 +124,10 @@ set(LIB PRIVATE bf::intern::atomic PRIVATE bf::intern::clog PRIVATE bf::intern::guardedalloc + PRIVATE bf::nodes PRIVATE bf::render PRIVATE bf::windowmanager + bf_compositor ) if(WITH_AUDASPACE) diff --git a/source/blender/sequencer/SEQ_modifier.hh b/source/blender/sequencer/SEQ_modifier.hh index c44a46439ba..ab98a35f766 100644 --- a/source/blender/sequencer/SEQ_modifier.hh +++ b/source/blender/sequencer/SEQ_modifier.hh @@ -53,7 +53,11 @@ struct StripModifierTypeInfo { /* Apply modifier on an image buffer. * quad contains four corners of the (pre-transform) strip rectangle in pixel space. */ - void (*apply)(const StripScreenQuad &quad, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask); + void (*apply)(const RenderData *render_data, + const StripScreenQuad &quad, + StripModifierData *smd, + ImBuf *ibuf, + ImBuf *mask); /** Register the panel types for the modifier's UI. */ void (*panel_register)(ARegionType *region_type); diff --git a/source/blender/sequencer/SEQ_modifiertypes.hh b/source/blender/sequencer/SEQ_modifiertypes.hh index 3928052d789..d607e1dc93c 100644 --- a/source/blender/sequencer/SEQ_modifiertypes.hh +++ b/source/blender/sequencer/SEQ_modifiertypes.hh @@ -17,6 +17,7 @@ namespace blender::seq { extern StripModifierTypeInfo seqModifierType_None; extern StripModifierTypeInfo seqModifierType_BrightContrast; extern StripModifierTypeInfo seqModifierType_ColorBalance; +extern StripModifierTypeInfo seqModifierType_Compositor; extern StripModifierTypeInfo seqModifierType_Curves; extern StripModifierTypeInfo seqModifierType_HueCorrect; extern StripModifierTypeInfo seqModifierType_Mask; diff --git a/source/blender/sequencer/SEQ_relations.hh b/source/blender/sequencer/SEQ_relations.hh index 6ef3878632a..a63cb2398e5 100644 --- a/source/blender/sequencer/SEQ_relations.hh +++ b/source/blender/sequencer/SEQ_relations.hh @@ -14,6 +14,7 @@ struct ListBase; struct Main; struct MovieClip; struct ReportList; +struct bNodeTree; struct Scene; struct Strip; @@ -53,6 +54,11 @@ void relations_invalidate_cache(Scene *scene, Strip *strip); void relations_invalidate_cache_raw(Scene *scene, Strip *strip); void relations_invalidate_scene_strips(const Main *bmain, const Scene *scene_target); +/** + * Invalidates the cache for all strips that uses the given node tree as a compositor modifier. + */ +void relations_invalidate_compositor_modifiers(const Main *bmain, const bNodeTree *node_tree); + void relations_invalidate_movieclip_strips(Main *bmain, MovieClip *clip_target); /** * Release FFmpeg handles of strips that are not currently displayed to minimize memory usage. diff --git a/source/blender/sequencer/SEQ_sequencer.hh b/source/blender/sequencer/SEQ_sequencer.hh index 40ca17c29ee..b08103073dc 100644 --- a/source/blender/sequencer/SEQ_sequencer.hh +++ b/source/blender/sequencer/SEQ_sequencer.hh @@ -144,6 +144,16 @@ Strip *lookup_strip_by_name(Editing *ed, const char *key); */ Span lookup_strips_by_scene(Editing *ed, const Scene *key); +/** + * Find all strips using provided compositor node tree as a modifier + * + * \param ed: Editing that owns lookup hash + * \param key: Node tree pointer + * + * \return Span of strips + */ +Span lookup_strips_by_compositor_node_group(Editing *ed, const bNodeTree *key); + /** * Find which meta strip the given timeline channel belongs to. Returns nullptr if it is a global * channel. diff --git a/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc b/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc index 2cd824de03e..a0a108f78dd 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc @@ -48,7 +48,8 @@ struct BrightContrastApplyOp { } }; -static void brightcontrast_apply(const StripScreenQuad & /*quad*/, +static void brightcontrast_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc b/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc index 2521ba4175a..998d784204f 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc @@ -244,7 +244,8 @@ static void colorBalance_init_data(StripModifierData *smd) } } -static void colorBalance_apply(const StripScreenQuad & /*quad*/, +static void colorBalance_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_compositor.cc b/source/blender/sequencer/intern/modifiers/MOD_compositor.cc new file mode 100644 index 00000000000..846e47d2e41 --- /dev/null +++ b/source/blender/sequencer/intern/modifiers/MOD_compositor.cc @@ -0,0 +1,242 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup sequencer + */ + +#include "BLI_math_base.h" +#include "BLI_rect.h" + +#include "BLT_translation.hh" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_evaluator.hh" + +#include "DNA_sequence_types.h" + +#include "DEG_depsgraph_query.hh" + +#include "IMB_colormanagement.hh" + +#include "SEQ_modifier.hh" +#include "SEQ_modifiertypes.hh" +#include "SEQ_render.hh" + +#include "UI_interface.hh" +#include "UI_interface_layout.hh" + +#include "RNA_access.hh" + +#include "modifier.hh" +#include "render.hh" + +namespace blender::seq { + +class CompositorContext : public compositor::Context { + private: + const RenderData &render_data_; + const SequencerCompositorModifierData *modifier_data_; + + ImBuf *image_buffer_; + ImBuf *mask_buffer_; + + public: + CompositorContext(const RenderData &render_data, + const SequencerCompositorModifierData *modifier_data, + ImBuf *image_buffer, + ImBuf *mask_buffer) + : compositor::Context(), + render_data_(render_data), + modifier_data_(modifier_data), + image_buffer_(image_buffer), + mask_buffer_(mask_buffer) + { + } + + const Scene &get_scene() const override + { + return *render_data_.scene; + } + + const bNodeTree &get_node_tree() const override + { + return *DEG_get_evaluated(render_data_.depsgraph, modifier_data_->node_group); + } + + compositor::OutputTypes needed_outputs() const override + { + compositor::OutputTypes needed_outputs = compositor::OutputTypes::Composite; + if (!render_data_.for_render) { + needed_outputs |= compositor::OutputTypes::Viewer; + } + return needed_outputs; + } + + bool treat_viewer_as_compositor_output() const override + { + return true; + } + + Bounds get_compositing_region() const override + { + return Bounds(int2(0), int2(image_buffer_->x, image_buffer_->y)); + } + + compositor::Result get_output() override + { + compositor::Result result = this->create_result(compositor::ResultType::Color); + result.wrap_external(image_buffer_->float_buffer.data, + int2(image_buffer_->x, image_buffer_->y)); + return result; + } + + compositor::Result get_viewer_output(compositor::Domain /*domain*/, + bool /*is_data*/, + compositor::ResultPrecision /*precision*/) override + { + compositor::Result result = this->create_result(compositor::ResultType::Color); + result.wrap_external(image_buffer_->float_buffer.data, + int2(image_buffer_->x, image_buffer_->y)); + return result; + } + + compositor::Result get_input(const Scene * /*scene*/, + int /*view_layer_id*/, + const char *pass_name) override + { + compositor::Result result = this->create_result(compositor::ResultType::Color); + + if (StringRef(pass_name) == "Image") { + result.wrap_external(image_buffer_->float_buffer.data, + int2(image_buffer_->x, image_buffer_->y)); + } + else if (StringRef(pass_name) == "Mask" && mask_buffer_) { + result.wrap_external(mask_buffer_->float_buffer.data, + int2(mask_buffer_->x, mask_buffer_->y)); + } + + return result; + } + + bool use_gpu() const override + { + return false; + } +}; + +static void compositor_modifier_init_data(StripModifierData *strip_modifier_data) +{ + SequencerCompositorModifierData *modifier_data = + reinterpret_cast(strip_modifier_data); + modifier_data->node_group = nullptr; +} + +static bool ensure_linear_float_buffer(ImBuf *ibuf) +{ + if (!ibuf) { + return false; + } + + /* Already have scene linear float pixels, nothing to do. */ + if (ibuf->float_buffer.data && + IMB_colormanagement_space_is_scene_linear(ibuf->float_buffer.colorspace)) + { + return true; + } + + if (ibuf->float_buffer.data == nullptr) { + IMB_float_from_byte(ibuf); + } + else { + const char *from_colorspace = IMB_colormanagement_get_float_colorspace(ibuf); + const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR); + IMB_colormanagement_transform_float(ibuf->float_buffer.data, + ibuf->x, + ibuf->y, + ibuf->channels, + from_colorspace, + to_colorspace, + true); + IMB_colormanagement_assign_float_colorspace(ibuf, to_colorspace); + } + return false; +} + +static void compositor_modifier_apply(const RenderData *render_data, + const StripScreenQuad & /*quad*/, + StripModifierData *strip_modifier_data, + ImBuf *image_buffer, + ImBuf *mask) +{ + const SequencerCompositorModifierData *modifier_data = + reinterpret_cast(strip_modifier_data); + if (!modifier_data->node_group) { + return; + } + + ensure_linear_float_buffer(mask); + const bool was_float_linear = ensure_linear_float_buffer(image_buffer); + const bool was_byte = image_buffer->float_buffer.data == nullptr; + + CompositorContext context(*render_data, modifier_data, image_buffer, mask); + compositor::Evaluator evaluator(context); + evaluator.evaluate(); + + if (was_float_linear) { + return; + } + + if (was_byte) { + IMB_byte_from_float(image_buffer); + IMB_free_float_pixels(image_buffer); + } + else { + seq_imbuf_to_sequencer_space(render_data->scene, image_buffer, true); + } +} + +static void compositor_modifier_panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA *ptr = UI_panel_custom_data_get(panel); + + layout->use_property_split_set(true); + + uiTemplateID(layout, + C, + ptr, + "node_group", + "NODE_OT_new_compositor_sequencer_node_group", + nullptr, + nullptr); + + if (uiLayout *mask_input_layout = layout->panel_prop( + C, ptr, "open_mask_input_panel", IFACE_("Mask Input"))) + { + draw_mask_input_type_settings(C, mask_input_layout, ptr); + } +} + +static void compositor_modifier_register(ARegionType *region_type) +{ + modifier_panel_register( + region_type, eSeqModifierType_Compositor, compositor_modifier_panel_draw); +} + +StripModifierTypeInfo seqModifierType_Compositor = { + /*idname*/ "Compositor", + /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Compositor"), + /*struct_name*/ "SequencerCompositorModifierData", + /*struct_size*/ sizeof(SequencerCompositorModifierData), + /*init_data*/ compositor_modifier_init_data, + /*free_data*/ nullptr, + /*copy_data*/ nullptr, + /*apply*/ compositor_modifier_apply, + /*panel_register*/ compositor_modifier_register, +}; + +}; // namespace blender::seq diff --git a/source/blender/sequencer/intern/modifiers/MOD_curves.cc b/source/blender/sequencer/intern/modifiers/MOD_curves.cc index 52e6945e633..465d200cda3 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_curves.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_curves.cc @@ -65,7 +65,8 @@ struct CurvesApplyOp { } }; -static void curves_apply(const StripScreenQuad & /*quad*/, +static void curves_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc b/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc index 82c855d472d..177dd0feb08 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc @@ -100,7 +100,8 @@ struct HueCorrectApplyOp { } }; -static void hue_correct_apply(const StripScreenQuad & /*quad*/, +static void hue_correct_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_mask.cc b/source/blender/sequencer/intern/modifiers/MOD_mask.cc index c9499d7bfc7..2474fe626f0 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_mask.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_mask.cc @@ -63,7 +63,8 @@ struct MaskApplyOp { } }; -static void maskmodifier_apply(const StripScreenQuad & /*quad*/, +static void maskmodifier_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData * /*smd*/, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc b/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc index 3c97ba1fb5c..f4660f9d5eb 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc @@ -280,7 +280,8 @@ static AreaLuminance tonemap_calc_input_luminance(const StripScreenQuad &quad, c return lum; } -static void tonemapmodifier_apply(const StripScreenQuad &quad, +static void tonemapmodifier_apply(const RenderData * /*render_data*/, + const StripScreenQuad &quad, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc b/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc index 871b1e1b404..e28e1d577c5 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc @@ -58,7 +58,8 @@ struct WhiteBalanceApplyOp { } }; -static void whiteBalance_apply(const StripScreenQuad & /*quad*/, +static void whiteBalance_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/modifier.cc b/source/blender/sequencer/intern/modifiers/modifier.cc index 8d20d49443d..1792dc2f675 100644 --- a/source/blender/sequencer/intern/modifiers/modifier.cc +++ b/source/blender/sequencer/intern/modifiers/modifier.cc @@ -371,6 +371,7 @@ static void modifier_types_init(StripModifierTypeInfo *types[]) INIT_TYPE(None); INIT_TYPE(BrightContrast); INIT_TYPE(ColorBalance); + INIT_TYPE(Compositor); INIT_TYPE(Curves); INIT_TYPE(HueCorrect); INIT_TYPE(Mask); @@ -528,7 +529,7 @@ void modifier_apply_stack(const RenderData *context, } ImBuf *mask = modifier_mask_get(smd, context, timeline_frame, frame_offset); - smti->apply(quad, smd, ibuf, mask); + smti->apply(context, quad, smd, ibuf, mask); if (mask) { IMB_freeImBuf(mask); } diff --git a/source/blender/sequencer/intern/strip_lookup.cc b/source/blender/sequencer/intern/strip_lookup.cc index 3be6171ca23..2681e1048ce 100644 --- a/source/blender/sequencer/intern/strip_lookup.cc +++ b/source/blender/sequencer/intern/strip_lookup.cc @@ -10,6 +10,7 @@ #include "sequencer.hh" #include "DNA_listBase.h" +#include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" @@ -29,6 +30,7 @@ static Mutex lookup_lock; struct StripLookup { blender::Map strip_by_name; blender::Map> strips_by_scene; + blender::Map> strips_by_compositor_node_group; blender::Map meta_by_strip; blender::Map> effects_by_strip; blender::Map owner_by_channel; @@ -55,6 +57,25 @@ static void strip_by_scene_lookup_build(Strip *strip, StripLookup *lookup) strips.add(strip); } +static void strip_by_compositor_node_group_lookup_build(Strip *strip, StripLookup *lookup) +{ + LISTBASE_FOREACH (StripModifierData *, modifier, &strip->modifiers) { + if (modifier->type != eSeqModifierType_Compositor) { + continue; + } + + const SequencerCompositorModifierData *modifier_data = + reinterpret_cast(modifier); + if (!modifier_data->node_group) { + continue; + } + + VectorSet &strips = lookup->strips_by_compositor_node_group.lookup_or_add_default( + modifier_data->node_group); + strips.add(strip); + } +} + static void strip_lookup_build_effect(Strip *strip, StripLookup *lookup) { if (!strip->is_effect()) { @@ -80,6 +101,7 @@ static void strip_lookup_build_from_seqbase(Strip *parent_meta, lookup->meta_by_strip.add(strip, parent_meta); strip_lookup_build_effect(strip, lookup); strip_by_scene_lookup_build(strip, lookup); + strip_by_compositor_node_group_lookup_build(strip, lookup); if (strip->type == STRIP_TYPE_META) { strip_lookup_build_from_seqbase(strip, &strip->seqbase, lookup); @@ -150,6 +172,18 @@ Span lookup_strips_by_scene(Editing *ed, const Scene *key) return strips.as_span(); } +Span lookup_strips_by_compositor_node_group(Editing *ed, const bNodeTree *key) +{ + BLI_assert(ed != nullptr); + BLI_assert(key->type == NTREE_COMPOSIT); + + std::lock_guard lock(lookup_lock); + strip_lookup_update_if_needed(ed, &ed->runtime.strip_lookup); + StripLookup *lookup = ed->runtime.strip_lookup; + VectorSet &strips = lookup->strips_by_compositor_node_group.lookup_or_add_default(key); + return strips.as_span(); +} + Strip *lookup_meta_by_strip(Editing *ed, const Strip *key) { BLI_assert(ed != nullptr); diff --git a/source/blender/sequencer/intern/strip_relations.cc b/source/blender/sequencer/intern/strip_relations.cc index 96dfb2bf35a..d46d47ef1f1 100644 --- a/source/blender/sequencer/intern/strip_relations.cc +++ b/source/blender/sequencer/intern/strip_relations.cc @@ -172,6 +172,17 @@ void relations_invalidate_scene_strips(const Main *bmain, const Scene *scene_tar } } +void relations_invalidate_compositor_modifiers(const Main *bmain, const bNodeTree *node_tree) +{ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != nullptr) { + for (Strip *strip : lookup_strips_by_compositor_node_group(editing_get(scene), node_tree)) { + relations_invalidate_cache(scene, strip); + } + } + } +} + static void invalidate_movieclip_strips(Scene *scene, MovieClip *clip_target, ListBase *seqbase) { for (Strip *strip = static_cast(seqbase->first); strip != nullptr; strip = strip->next)