From 7311b79bc7f66c49bf3ece274020075bb0c0a993 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Mon, 12 Jun 2023 16:37:19 +0300 Subject: [PATCH 1/2] Nodes: Add bNodeInstanceKey to DerivedNodeTree This patch adds support for bNodeInstanceKey to DerivedNodeTree. The keys are computed and cached in the DContext at construction time, and various utilities are added to return the instance keys of contexts, nodes, as well as find the active context in the tree. --- .../realtime_compositor/intern/scheduler.cc | 64 ++----------------- source/blender/nodes/NOD_derived_node_tree.hh | 20 +++++- .../blender/nodes/intern/derived_node_tree.cc | 61 +++++++++++++++++- 3 files changed, 84 insertions(+), 61 deletions(-) diff --git a/source/blender/compositor/realtime_compositor/intern/scheduler.cc b/source/blender/compositor/realtime_compositor/intern/scheduler.cc index a08d90b163e..f6a424ccd7d 100644 --- a/source/blender/compositor/realtime_compositor/intern/scheduler.cc +++ b/source/blender/compositor/realtime_compositor/intern/scheduler.cc @@ -21,58 +21,6 @@ namespace blender::realtime_compositor { using namespace nodes::derived_node_tree_types; -/* Find the active context from the given context and its descendants contexts. The active context - * is the one whose node instance key matches the active_viewer_key stored in the root node tree. - * The instance key of each context is computed by calling BKE_node_instance_key given the key of - * the parent as well as the group node making the context. */ -static const DTreeContext *find_active_context_recursive(const DTreeContext *context, - bNodeInstanceKey key) -{ - /* The instance key of the given context matches the active viewer instance key, so this is the - * active context, return it. */ - if (key.value == context->derived_tree().root_context().btree().active_viewer_key.value) { - return context; - } - - /* For each of the group nodes, compute their instance key and contexts and call this function - * recursively. */ - for (const bNode *group_node : context->btree().group_nodes()) { - const bNodeInstanceKey child_key = BKE_node_instance_key(key, &context->btree(), group_node); - const DTreeContext *child_context = context->child_context(*group_node); - const DTreeContext *found_context = find_active_context_recursive(child_context, child_key); - - /* If the found context is null, that means neither the child context nor one of its descendant - * contexts is active. */ - if (!found_context) { - continue; - } - - /* Otherwise, we have found our active context, return it. */ - return found_context; - } - - /* Neither the given context nor one of its descendant contexts is active, so return null. */ - return nullptr; -} - -/* Find the active context for the given node tree. The active context represents the node tree - * currently being edited. In most cases, that would be the top level node tree itself, but in the - * case where the user is editing the node tree of a node group, the active context would be a - * representation of the node tree of that node group. Note that the context also stores the group - * node that the user selected to edit the node tree, so the context fully represents a particular - * instance of the node group. */ -static const DTreeContext *find_active_context(const DerivedNodeTree &tree) -{ - /* If the active viewer key is NODE_INSTANCE_KEY_NONE, that means it is not yet initialized and - * we return the root context in that case. See the find_active_context_recursive function. */ - if (tree.root_context().btree().active_viewer_key.value == NODE_INSTANCE_KEY_NONE.value) { - return &tree.root_context(); - } - - /* The root context has an instance key of NODE_INSTANCE_KEY_BASE by definition. */ - return find_active_context_recursive(&tree.root_context(), NODE_INSTANCE_KEY_BASE); -} - /* Add the viewer node which is marked as NODE_DO_OUTPUT in the given context to the given stack. * If multiple types of viewer nodes are marked, then the preference will be CMP_NODE_VIEWER > * CMP_NODE_SPLITVIEWER. If no viewer nodes were found, composite nodes can be added as a fallback @@ -114,7 +62,8 @@ static bool add_viewer_nodes_in_context(const DTreeContext *context, Stack &node_stack) @@ -139,8 +88,8 @@ static void add_output_nodes(const Context &context, } } - const DTreeContext *active_context = find_active_context(tree); - const bool viewer_was_added = add_viewer_nodes_in_context(active_context, node_stack); + const DTreeContext &active_context = tree.active_context(); + const bool viewer_was_added = add_viewer_nodes_in_context(&active_context, node_stack); /* An active viewer was added, no need to search further. */ if (viewer_was_added) { @@ -149,7 +98,7 @@ static void add_output_nodes(const Context &context, /* If the active context is the root one and no viewer nodes were found, we consider this node * tree to have no viewer nodes, even if one of the non-active descendants have viewer nodes. */ - if (active_context->is_root()) { + if (active_context.is_root()) { return; } @@ -394,7 +343,8 @@ Schedule compute_schedule(const Context &context, const DerivedNodeTree &tree) int insertion_position = 0; for (int i = 0; i < sorted_dependency_nodes.size(); i++) { if (needed_buffers.lookup(doutput.node()) > - needed_buffers.lookup(sorted_dependency_nodes[i])) { + needed_buffers.lookup(sorted_dependency_nodes[i])) + { insertion_position++; } else { diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index 45609de213c..43ff08b4fc2 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -46,6 +46,8 @@ class DTreeContext { const bNode *parent_node_; /* The current node tree. */ const bNodeTree *btree_; + /* The instance key of the parent node. NODE_INSTANCE_KEY_BASE for root contexts. */ + bNodeInstanceKey instance_key_; /* All the children contexts of this context. */ Map children_; DerivedNodeTree *derived_tree_; @@ -56,6 +58,7 @@ class DTreeContext { const bNodeTree &btree() const; const DTreeContext *parent_context() const; const bNode *parent_node() const; + const bNodeInstanceKey instance_key() const; const DTreeContext *child_context(const bNode &node) const; const DerivedNodeTree &derived_tree() const; bool is_root() const; @@ -76,6 +79,7 @@ class DNode { const DTreeContext *context() const; const bNode *bnode() const; + const bNodeInstanceKey instance_key() const; const bNode *operator->() const; const bNode &operator*() const; @@ -191,6 +195,14 @@ class DerivedNodeTree { const DTreeContext &root_context() const; Span used_btrees() const; + /** Returns the active context for the node tree. The active context represents the node tree + * currently being edited. In most cases, that would be the top level node tree itself, but in + * the case where the user is editing the node tree of a node group, the active context would be + * a representation of the node tree of that node group. Note that the context also stores the + * group node that the user selected to edit the node tree, so the context fully represents a + * particular instance of the node group. */ + const DTreeContext &active_context() const; + /** * \return True when there is a link cycle. Unavailable sockets are ignored. */ @@ -205,7 +217,8 @@ class DerivedNodeTree { private: DTreeContext &construct_context_recursively(DTreeContext *parent_context, const bNode *parent_node, - const bNodeTree &btree); + const bNodeTree &btree, + const bNodeInstanceKey instance_key); void destruct_context_recursively(DTreeContext *context); void foreach_node_in_context_recursive(const DTreeContext &context, @@ -240,6 +253,11 @@ inline const bNode *DTreeContext::parent_node() const return parent_node_; } +inline const bNodeInstanceKey DTreeContext::instance_key() const +{ + return instance_key_; +} + inline const DTreeContext *DTreeContext::child_context(const bNode &node) const { return children_.lookup_default(&node, nullptr); diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index fee7e90dc8b..dceac0ddf70 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -15,12 +15,14 @@ DerivedNodeTree::DerivedNodeTree(const bNodeTree &btree) /* Construct all possible contexts immediately. This is significantly cheaper than inlining all * node groups. If it still becomes a performance issue in the future, contexts could be * constructed lazily when they are needed. */ - root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree); + root_context_ = &this->construct_context_recursively( + nullptr, nullptr, btree, NODE_INSTANCE_KEY_BASE); } DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context, const bNode *parent_node, - const bNodeTree &btree) + const bNodeTree &btree, + const bNodeInstanceKey instance_key) { btree.ensure_topology_cache(); DTreeContext &context = *allocator_.construct().release(); @@ -28,13 +30,16 @@ DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *paren context.parent_node_ = parent_node; context.derived_tree_ = this; context.btree_ = &btree; + context.instance_key_ = instance_key; used_btrees_.add(context.btree_); for (const bNode *bnode : context.btree_->all_nodes()) { if (bnode->is_group()) { bNodeTree *child_btree = reinterpret_cast(bnode->id); if (child_btree != nullptr) { - DTreeContext &child = this->construct_context_recursively(&context, bnode, *child_btree); + const bNodeInstanceKey child_key = BKE_node_instance_key(instance_key, &btree, bnode); + DTreeContext &child = this->construct_context_recursively( + &context, bnode, *child_btree, child_key); context.children_.add_new(bnode, &child); } } @@ -93,6 +98,11 @@ void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &cont } } +const bNodeInstanceKey DNode::instance_key() const +{ + return BKE_node_instance_key(context()->instance_key(), &context()->btree(), bnode()); +} + DOutputSocket DInputSocket::get_corresponding_group_node_output() const { BLI_assert(*this); @@ -299,6 +309,51 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, } } +/* Find the active context from the given context and its descendants contexts. The active context + * is the one whose node instance key matches the active_viewer_key stored in the root node tree. + * The instance key of each context is computed by calling BKE_node_instance_key given the key of + * the parent as well as the group node making the context. */ +static const DTreeContext *find_active_context_recursive(const DTreeContext *context) +{ + const bNodeInstanceKey key = context->instance_key(); + + /* The instance key of the given context matches the active viewer instance key, so this is the + * active context, return it. */ + if (key.value == context->derived_tree().root_context().btree().active_viewer_key.value) { + return context; + } + + /* For each of the group nodes, compute their instance key and contexts and call this function + * recursively. */ + for (const bNode *group_node : context->btree().group_nodes()) { + const DTreeContext *child_context = context->child_context(*group_node); + const DTreeContext *found_context = find_active_context_recursive(child_context); + + /* If the found context is null, that means neither the child context nor one of its descendant + * contexts is active. */ + if (!found_context) { + continue; + } + + /* Otherwise, we have found our active context, return it. */ + return found_context; + } + + /* Neither the given context nor one of its descendant contexts is active, so return null. */ + return nullptr; +} + +const DTreeContext &DerivedNodeTree::active_context() const +{ + /* If the active viewer key is NODE_INSTANCE_KEY_NONE, that means it is not yet initialized and + * we return the root context in that case. See the find_active_context_recursive function. */ + if (root_context().btree().active_viewer_key.value == NODE_INSTANCE_KEY_NONE.value) { + return root_context(); + } + + return *find_active_context_recursive(&root_context()); +} + /* Each nested node group gets its own cluster. Just as node groups, clusters can be nested. */ static dot::Cluster *get_dot_cluster_for_context( dot::DirectedGraph &digraph, -- 2.30.2 From 997855461b3ba50636a281fc42c20a9b45194438 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Mon, 12 Jun 2023 18:41:49 +0300 Subject: [PATCH 2/2] Realtime Compositor: Add support for node previews This patch adds support for node previews in the realtime compositor. Only node operations have previews for now. Shader nodes likes the MixRGB node does not have previews implemented yet due to required sizable changes in the node compiler. Depends on: #108900. --- .../realtime_compositor/CMakeLists.txt | 2 + .../realtime_compositor/COM_context.hh | 3 + .../realtime_compositor/COM_node_operation.hh | 18 +++ .../realtime_compositor/COM_operation.hh | 4 + .../intern/node_operation.cc | 134 ++++++++++++++++++ .../realtime_compositor/intern/operation.cc | 4 + .../shaders/compositor_compute_preview.glsl | 6 + .../infos/compositor_compute_preview_info.hh | 12 ++ .../engines/compositor/compositor_engine.cc | 5 + source/blender/render/intern/compositor.cc | 5 + 10 files changed, 193 insertions(+) create mode 100644 source/blender/compositor/realtime_compositor/shaders/compositor_compute_preview.glsl create mode 100644 source/blender/compositor/realtime_compositor/shaders/infos/compositor_compute_preview_info.hh diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index 257ba734631..daa1c354407 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -112,6 +112,7 @@ set(GLSL_SRC shaders/compositor_blur_variable_size.glsl shaders/compositor_bokeh_image.glsl shaders/compositor_box_mask.glsl + shaders/compositor_compute_preview.glsl shaders/compositor_convert.glsl shaders/compositor_despeckle.glsl shaders/compositor_directional_blur.glsl @@ -226,6 +227,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/compositor_blur_variable_size_info.hh shaders/infos/compositor_bokeh_image_info.hh shaders/infos/compositor_box_mask_info.hh + shaders/infos/compositor_compute_preview_info.hh shaders/infos/compositor_convert_info.hh shaders/infos/compositor_despeckle_info.hh shaders/infos/compositor_directional_blur_info.hh diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh index 945aa17f6d8..0aaf30c21a8 100644 --- a/source/blender/compositor/realtime_compositor/COM_context.hh +++ b/source/blender/compositor/realtime_compositor/COM_context.hh @@ -42,6 +42,9 @@ class Context { public: Context(TexturePool &texture_pool); + /* Get the compositing scene. */ + virtual const Scene &get_scene() const = 0; + /* Get the node tree used for compositing. */ virtual const bNodeTree &get_node_tree() const = 0; diff --git a/source/blender/compositor/realtime_compositor/COM_node_operation.hh b/source/blender/compositor/realtime_compositor/COM_node_operation.hh index 1779e21871d..2771a4df856 100644 --- a/source/blender/compositor/realtime_compositor/COM_node_operation.hh +++ b/source/blender/compositor/realtime_compositor/COM_node_operation.hh @@ -12,6 +12,7 @@ #include "COM_context.hh" #include "COM_operation.hh" +#include "COM_result.hh" #include "COM_scheduler.hh" namespace blender::realtime_compositor { @@ -44,6 +45,12 @@ class NodeOperation : public Operation { void compute_results_reference_counts(const Schedule &schedule); protected: + /* Compute a preview for the operation and set to the bNodePreview of the node. This is only done + * for nodes which enables previews, are not hidden, and are part of the active node context. The + * preview is computed as a lower resolution version of the output of the get_preview_result + * method. */ + void compute_preview() override; + /* Returns a reference to the derived node that this operation represents. */ const DNode &node() const; @@ -53,6 +60,17 @@ class NodeOperation : public Operation { /* Returns true if the output identified by the given identifier is needed and should be * computed, otherwise returns false. */ bool should_compute_output(StringRef identifier); + + private: + /* Get the result which will be previewed in the node, this is chosen as the first linked output + * of the node, if no outputs exist, then the first allocated input will be chosen. Nullptr is + * guaranteed not to be returned, since the node will always either have a linked output or an + * allocated input. */ + Result *get_preview_result(); + + /* Resize the give input result to the given preview size and set it to the preview buffer after + * applying the necessary color management processor.*/ + void write_preview_from_result(bNodePreview &preview, Result &input_result); }; } // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_operation.hh b/source/blender/compositor/realtime_compositor/COM_operation.hh index 444177db8a5..8b9becc5593 100644 --- a/source/blender/compositor/realtime_compositor/COM_operation.hh +++ b/source/blender/compositor/realtime_compositor/COM_operation.hh @@ -130,6 +130,10 @@ class Operation { * output results. */ virtual void execute() = 0; + /* Compute and set a preview of the operation if needed. This method defaults to an empty + * implementation and should be implemented by operations which can have previews. */ + virtual void compute_preview(); + /* Get a reference to the result connected to the input identified by the given identifier. */ Result &get_input(StringRef identifier) const; diff --git a/source/blender/compositor/realtime_compositor/intern/node_operation.cc b/source/blender/compositor/realtime_compositor/intern/node_operation.cc index 8fb9ed0914d..527576e6c33 100644 --- a/source/blender/compositor/realtime_compositor/intern/node_operation.cc +++ b/source/blender/compositor/realtime_compositor/intern/node_operation.cc @@ -4,15 +4,29 @@ #include +#include "BLI_assert.h" +#include "BLI_index_range.hh" #include "BLI_map.hh" +#include "BLI_math_base.h" +#include "BLI_math_base.hh" +#include "BLI_math_color.h" +#include "BLI_math_vector_types.hh" #include "BLI_string_ref.hh" +#include "BLI_task.hh" #include "BLI_vector.hh" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "IMB_colormanagement.h" + #include "DNA_node_types.h" #include "NOD_derived_node_tree.hh" #include "NOD_node_declaration.hh" +#include "BKE_node.h" + #include "COM_context.hh" #include "COM_input_descriptor.hh" #include "COM_node_operation.hh" @@ -39,6 +53,126 @@ NodeOperation::NodeOperation(Context &context, DNode node) : Operation(context), } } +/* Given the size of a result, compute a lower resolution size for a preview. The greater dimension + * will be assigned an arbitrarily chosen size of 128, while the other dimension will get the size + * that maintains the same aspect ratio. */ +static int2 compute_preview_size(int2 size) +{ + const int greater_dimension_size = 128; + if (size.x > size.y) { + return int2(greater_dimension_size, int(greater_dimension_size * (float(size.y) / size.x))); + } + else { + return int2(int(greater_dimension_size * (float(size.x) / size.y)), greater_dimension_size); + } +} + +void NodeOperation::compute_preview() +{ + if (!(node()->flag & NODE_PREVIEW)) { + return; + } + + if (node()->flag & NODE_HIDDEN) { + return; + } + + /* Only compute previews for nodes in the active context. */ + if (node().context()->instance_key().value != + node().context()->derived_tree().active_context().instance_key().value) + { + return; + } + + /* Initialize node tree previews if not already initialized. */ + bNodeTree *root_tree = const_cast( + &node().context()->derived_tree().root_context().btree()); + if (!root_tree->previews) { + root_tree->previews = BKE_node_instance_hash_new("node previews"); + } + + Result *preview_result = get_preview_result(); + const int2 preview_size = compute_preview_size(preview_result->domain().size); + node()->runtime->preview_xsize = preview_size.x; + node()->runtime->preview_ysize = preview_size.y; + + bNodePreview *preview = bke::node_preview_verify( + root_tree->previews, node().instance_key(), preview_size.x, preview_size.y, true); + + write_preview_from_result(*preview, *preview_result); +} + +Result *NodeOperation::get_preview_result() +{ + /* Find the first linked output. */ + for (const bNodeSocket *output : node()->output_sockets()) { + Result &output_result = get_result(output->identifier); + if (output_result.should_compute()) { + return &output_result; + } + } + + /* No linked outputs, find the first allocated input. */ + for (const bNodeSocket *input : node()->input_sockets()) { + Result &input_result = get_input(input->identifier); + if (input_result.is_allocated()) { + return &input_result; + } + } + + BLI_assert_unreachable(); + return nullptr; +} + +void NodeOperation::write_preview_from_result(bNodePreview &preview, Result &input_result) +{ + GPUShader *shader = shader_manager().get("compositor_compute_preview"); + GPU_shader_bind(shader); + + if (input_result.type() == ResultType::Float) { + GPU_texture_swizzle_set(input_result.texture(), "rrr1"); + } + + input_result.bind_as_texture(shader, "input_tx"); + + const int2 preview_size = int2(preview.xsize, preview.ysize); + Result preview_result = Result::Temporary(ResultType::Color, texture_pool()); + preview_result.allocate_texture(Domain(preview_size)); + preview_result.bind_as_image(shader, "preview_img"); + + compute_dispatch_threads_at_least(shader, preview_size); + + input_result.unbind_as_texture(); + preview_result.unbind_as_image(); + GPU_shader_unbind(); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + float *preview_pixels = static_cast( + GPU_texture_read(preview_result.texture(), GPU_DATA_FLOAT, 0)); + preview_result.release(); + + ColormanageProcessor *color_processor = IMB_colormanagement_display_processor_new( + &context().get_scene().view_settings, &context().get_scene().display_settings); + + threading::parallel_for(IndexRange(preview_size.y), 1, [&](const IndexRange sub_y_range) { + for (const int64_t y : sub_y_range) { + for (const int64_t x : IndexRange(preview_size.x)) { + const int index = (y * preview_size.x + x) * 4; + IMB_colormanagement_processor_apply_v4(color_processor, preview_pixels + index); + rgba_float_to_uchar(preview.rect + index, preview_pixels + index); + } + } + }); + + /* Restore original swizzle mask set above. */ + if (input_result.type() == ResultType::Float) { + GPU_texture_swizzle_set(input_result.texture(), "rgba"); + } + + IMB_colormanagement_processor_free(color_processor); + MEM_freeN(preview_pixels); +} + void NodeOperation::compute_results_reference_counts(const Schedule &schedule) { for (const bNodeSocket *output : this->node()->output_sockets()) { diff --git a/source/blender/compositor/realtime_compositor/intern/operation.cc b/source/blender/compositor/realtime_compositor/intern/operation.cc index d257328559b..2d7fcf9b718 100644 --- a/source/blender/compositor/realtime_compositor/intern/operation.cc +++ b/source/blender/compositor/realtime_compositor/intern/operation.cc @@ -35,6 +35,8 @@ void Operation::evaluate() execute(); + compute_preview(); + release_inputs(); release_unneeded_results(); @@ -136,6 +138,8 @@ void Operation::add_and_evaluate_input_processor(StringRef identifier, SimpleOpe processor->evaluate(); } +void Operation::compute_preview(){}; + Result &Operation::get_input(StringRef identifier) const { return *results_mapped_to_inputs_.lookup(identifier); diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_compute_preview.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_compute_preview.glsl new file mode 100644 index 00000000000..fb3aae3f9c0 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_compute_preview.glsl @@ -0,0 +1,6 @@ +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(preview_img)); + imageStore(preview_img, texel, texture(input_tx, coordinates)); +} diff --git a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_compute_preview_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_compute_preview_info.hh new file mode 100644 index 00000000000..2593ec41d81 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_compute_preview_info.hh @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_compute_preview) + .local_group_size(16, 16) + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "preview_img") + .compute_source("compositor_compute_preview.glsl") + .do_static_compilation(true); diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc index a7fc9fa3181..773a57b1cf7 100644 --- a/source/blender/draw/engines/compositor/compositor_engine.cc +++ b/source/blender/draw/engines/compositor/compositor_engine.cc @@ -58,6 +58,11 @@ class Context : public realtime_compositor::Context { { } + const Scene &get_scene() const override + { + return *DRW_context_state_get()->scene; + } + const bNodeTree &get_node_tree() const override { return *DRW_context_state_get()->scene->nodetree; diff --git a/source/blender/render/intern/compositor.cc b/source/blender/render/intern/compositor.cc index b94e94cb27a..cc16d4181bd 100644 --- a/source/blender/render/intern/compositor.cc +++ b/source/blender/render/intern/compositor.cc @@ -95,6 +95,11 @@ class Context : public realtime_compositor::Context { GPU_TEXTURE_FREE_SAFE(viewer_output_texture_); } + const Scene &get_scene() const override + { + return scene_; + } + const bNodeTree &get_node_tree() const override { return node_tree_; -- 2.30.2