From 3081bf63eeb0bc2fb3f271698019463e2f6af7e3 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Thu, 14 May 2026 14:58:51 +0200 Subject: [PATCH] Compositor: Support value inspection for image pixel operation This patch supports value inspection for image pixel operations as a follow up to ffd88022c9. This is done by logging the operation domain and context precision for all internal non single value inputs and outputs. This does not yet support single value pixel operations. Pull Request: https://projects.blender.org/blender/blender/pulls/158581 --- .../COM_multi_function_procedure_operation.hh | 3 - .../blender/compositor/COM_pixel_operation.hh | 9 +- .../multi_function_procedure_operation.cc | 5 +- .../compositor/intern/pixel_operation.cc | 90 ++++++++++++++++++- .../compositor/intern/shader_operation.cc | 2 +- 5 files changed, 96 insertions(+), 13 deletions(-) diff --git a/source/blender/compositor/COM_multi_function_procedure_operation.hh b/source/blender/compositor/COM_multi_function_procedure_operation.hh index bc458b581db..bc703017543 100644 --- a/source/blender/compositor/COM_multi_function_procedure_operation.hh +++ b/source/blender/compositor/COM_multi_function_procedure_operation.hh @@ -57,9 +57,6 @@ class MultiFunctionProcedureOperation : public PixelOperation { * output results for each of the parameters in the procedure. Note that parameters have no * identifiers and are identified solely by their order. */ Vector parameter_identifiers_; - /* True if the operation operates on single values, that is, all of its inputs and outputs are - * single values. */ - const bool is_single_value_; public: /* Build a multi-function procedure as well as an executor for it from the given pixel compile diff --git a/source/blender/compositor/COM_pixel_operation.hh b/source/blender/compositor/COM_pixel_operation.hh index efc573e8650..c4a99cc2b11 100644 --- a/source/blender/compositor/COM_pixel_operation.hh +++ b/source/blender/compositor/COM_pixel_operation.hh @@ -100,14 +100,19 @@ class PixelOperation : public Operation { /* A vector set that stores all output sockets that are used as previews for nodes inside the * pixel operation. */ VectorSet preview_outputs_; + /* True if the operation operates on single values, that is, all of its inputs and outputs are + * single values. */ + const bool is_single_value_; public: PixelOperation(Context &context, PixelCompileUnit &compile_unit, const Schedule &schedule, - const ComputeContext &compute_context); + const ComputeContext &compute_context, + const bool is_single_value); - /* Log a node preview for all nodes in the pixel operations if the node requires a preview. + /* Log the values of all inputs and outputs of nodes inside the pixel operation as well as node + * previews for all nodes in the pixel operations if the node requires a preview. * * Previews are computed from results that are populated for outputs that are used to compute * previews even if they are internally linked, and those outputs are stored and tracked in the diff --git a/source/blender/compositor/intern/multi_function_procedure_operation.cc b/source/blender/compositor/intern/multi_function_procedure_operation.cc index 025af0ee83a..e823e53ab28 100644 --- a/source/blender/compositor/intern/multi_function_procedure_operation.cc +++ b/source/blender/compositor/intern/multi_function_procedure_operation.cc @@ -51,9 +51,8 @@ MultiFunctionProcedureOperation::MultiFunctionProcedureOperation( const Schedule &schedule, const bool is_single_value, const ComputeContext &compute_context) - : PixelOperation(context, compile_unit, schedule, compute_context), - procedure_builder_(procedure_), - is_single_value_(is_single_value) + : PixelOperation(context, compile_unit, schedule, compute_context, is_single_value), + procedure_builder_(procedure_) { this->build_procedure(); procedure_executor_ = std::make_unique(procedure_); diff --git a/source/blender/compositor/intern/pixel_operation.cc b/source/blender/compositor/intern/pixel_operation.cc index e38661c18e8..02aea512abe 100644 --- a/source/blender/compositor/intern/pixel_operation.cc +++ b/source/blender/compositor/intern/pixel_operation.cc @@ -29,21 +29,103 @@ namespace blender::compositor { PixelOperation::PixelOperation(Context &context, PixelCompileUnit &compile_unit, const Schedule &schedule, - const ComputeContext &compute_context) + const ComputeContext &compute_context, + const bool is_single_value) : Operation(context), compile_unit_(compile_unit), schedule_(schedule), - compute_context_(compute_context) + compute_context_(compute_context), + is_single_value_(is_single_value) { } +static destruct_ptr get_image_info_log( + LinearAllocator<> *allocator, const Domain &domain, const ResultPrecision &precision) +{ + return allocator->construct( + domain.data_size, + domain.display_size, + domain.data_offset, + domain.transformation, + to_string(domain.realization_options.interpolation), + to_string(domain.realization_options.extension_x), + to_string(domain.realization_options.extension_y), + to_string(precision)); +} + void PixelOperation::log_data() { + /* No logging for single-value pixel operations for now. */ + if (is_single_value_) { + return; + } + + nodes::eval_log::NodesEvalLog *log = this->context().nodes_evaluation_log(); + if (!log) { + return; + } + nodes::eval_log::NodeTreeLogger &tree_logger = log->get_local_tree_logger(compute_context_); + + const Domain domain = this->compute_domain(); + + /* All inputs and outputs of pixel operations operate in the same domain, so the operation domain + * should be logged for all. The exception is inputs that are single values, in which case, their + * value is simply logged. */ + for (const bNode *node : compile_unit_) { + /* Log output values. */ + for (const bNodeSocket *output_socket : node->output_sockets()) { + if (!is_socket_available(output_socket)) { + continue; + } + + if (!output_socket->is_logically_linked()) { + continue; + } + + tree_logger.output_socket_values.append( + *tree_logger.allocator, + {node->identifier, + output_socket->index(), + get_image_info_log(tree_logger.allocator, domain, this->context().get_precision())}); + } + + /* Log input values. */ + for (const bNodeSocket *input_socket : node->input_sockets()) { + if (!is_socket_available(input_socket)) { + continue; + } + + if (!input_socket->is_logically_linked()) { + continue; + } + + /* The input is linked to a node that is inside the pixel operation, so skip it since it will + * inherit its value from an output that was logged above. */ + const bNodeSocket &linked_output = *input_socket->logically_linked_sockets()[0]; + if (compile_unit_.contains(&linked_output.owner_node())) { + continue; + } + + /* Otherwise, it is linked to a node that is outside of the compile unit. If it is a single + * value, log that single value, if not, we log the operation domain. */ + const std::string &input_identifier = outputs_to_declared_inputs_map_.lookup(&linked_output); + const Result &input = this->get_input(input_identifier); + if (input.is_single_value()) { + tree_logger.log_value(*node, *input_socket, input.single_value()); + continue; + } + + tree_logger.input_socket_values.append( + *tree_logger.allocator, + {node->identifier, + input_socket->index(), + get_image_info_log(tree_logger.allocator, domain, this->context().get_precision())}); + } + } + for (const bNodeSocket *output : preview_outputs_) { Result &result = this->get_result(get_output_identifier_from_output_socket(*output)); ImBuf *preview = compositor::compute_preview(context(), result); - nodes::eval_log::NodeTreeLogger &tree_logger = - this->context().nodes_evaluation_log()->get_local_tree_logger(compute_context_); tree_logger.node_image_previews.append(*tree_logger.allocator, {output->owner_node().identifier, preview}); diff --git a/source/blender/compositor/intern/shader_operation.cc b/source/blender/compositor/intern/shader_operation.cc index 202c6c6d5b7..2ea7d25a948 100644 --- a/source/blender/compositor/intern/shader_operation.cc +++ b/source/blender/compositor/intern/shader_operation.cc @@ -48,7 +48,7 @@ ShaderOperation::ShaderOperation(Context &context, PixelCompileUnit &compile_unit, const Schedule &schedule, const ComputeContext &compute_context) - : PixelOperation(context, compile_unit, schedule, compute_context) + : PixelOperation(context, compile_unit, schedule, compute_context, false) { material_ = GPU_material_from_callbacks( GPU_MAT_COMPOSITOR, &construct_material, &generate_code, this);