mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Compositor: Support socket value inspection
This patch adds support for socket value inspection in the compositor. This displays the following information: - If the image has a display window, the data and display windows are displayed. If not, the image resolution is displayed. - If the image is transformed, its translation, rotation, and scale are displayed. If not, it will say Identity. - The sampling options are displayed, including interpolation and sampling modes. - The precision of the data, Full or Half. Pixel nodes are not yet supported in this PR. Pull Request: https://projects.blender.org/blender/blender/pulls/158546
This commit is contained in:
parent
17e76a69d4
commit
ffd88022c9
13 changed files with 256 additions and 19 deletions
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
|
|
@ -204,6 +205,8 @@ class Domain {
|
|||
bool operator==(const Domain &a, const Domain &b);
|
||||
bool operator!=(const Domain &a, const Domain &b);
|
||||
|
||||
StringRefNull to_string(const Interpolation &interpolation);
|
||||
StringRefNull to_string(const Extension &extension);
|
||||
GPUSamplerExtendMode map_extension_mode_to_extend_mode(const Extension &mode);
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ class NodeOperation : public Operation {
|
|||
void set_needs_node_previews(const bool needed);
|
||||
|
||||
protected:
|
||||
/* Compute a node preview using the result returned from the get_preview_result method. */
|
||||
void compute_preview() override;
|
||||
/* Log the values for the inputs and outputs of the node as well as its image preview. */
|
||||
void log_data() override;
|
||||
|
||||
/* Returns a reference to the node that this operation represents. */
|
||||
const bNode &node() const;
|
||||
|
|
|
|||
|
|
@ -114,9 +114,8 @@ 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();
|
||||
/* Log the data of the operation into the context logger. */
|
||||
virtual void log_data();
|
||||
|
||||
/* Add the given result to the results_ map identified by the given output identifier. This
|
||||
* should be called during operation construction for all outputs. The provided result shouldn't
|
||||
|
|
|
|||
|
|
@ -107,12 +107,12 @@ class PixelOperation : public Operation {
|
|||
const Schedule &schedule,
|
||||
const ComputeContext &compute_context);
|
||||
|
||||
/* Compute a node preview for all nodes in the pixel operations if the node requires a preview.
|
||||
/* Log a node preview 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
|
||||
* preview_outputs_ vector set, see the populate_results_for_node method for more information. */
|
||||
void compute_preview() override;
|
||||
void log_data() override;
|
||||
|
||||
/* Get the identifier of the operation output corresponding to the given output socket. This is
|
||||
* called by the compiler to identify the operation output that provides the result for an input
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_memory_utils.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
|
@ -482,6 +483,8 @@ class Result {
|
|||
int64_t get_pixel_index(const int2 &texel) const;
|
||||
};
|
||||
|
||||
StringRefNull to_string(const ResultPrecision &precision);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Inline Methods.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "COM_domain.hh"
|
||||
|
|
@ -107,6 +109,38 @@ bool operator!=(const Domain &a, const Domain &b)
|
|||
return !(a == b);
|
||||
}
|
||||
|
||||
StringRefNull to_string(const Interpolation &interpolation)
|
||||
{
|
||||
switch (interpolation) {
|
||||
case Interpolation::Nearest:
|
||||
return N_("Nearest");
|
||||
case Interpolation::Bilinear:
|
||||
return N_("Bilinear");
|
||||
case Interpolation::Bicubic:
|
||||
return N_("Bicubic");
|
||||
case Interpolation::Anisotropic:
|
||||
return N_("Anisotropic");
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return "None";
|
||||
}
|
||||
|
||||
StringRefNull to_string(const Extension &extension)
|
||||
{
|
||||
switch (extension) {
|
||||
case Extension::Extend:
|
||||
return N_("Extend");
|
||||
case Extension::Repeat:
|
||||
return N_("Repeat");
|
||||
case Extension::Clip:
|
||||
return N_("Clip");
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return "None";
|
||||
}
|
||||
|
||||
GPUSamplerExtendMode map_extension_mode_to_extend_mode(const Extension &mode)
|
||||
{
|
||||
switch (mode) {
|
||||
|
|
|
|||
|
|
@ -131,21 +131,81 @@ void NodeOperation::set_needs_node_previews(const bool needed)
|
|||
needs_node_previews_ = needed;
|
||||
}
|
||||
|
||||
void NodeOperation::compute_preview()
|
||||
static destruct_ptr<nodes::eval_log::ImageInfoLog> get_image_info_log(LinearAllocator<> *allocator,
|
||||
const Result &result)
|
||||
{
|
||||
if (!needs_node_previews_ || !is_node_preview_needed(this->node())) {
|
||||
const Domain &domain = result.domain();
|
||||
return allocator->construct<nodes::eval_log::ImageInfoLog>(
|
||||
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(result.precision()));
|
||||
}
|
||||
|
||||
void NodeOperation::log_data()
|
||||
{
|
||||
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 Result *result = this->get_preview_result();
|
||||
if (!result || result->is_single_value()) {
|
||||
return;
|
||||
/* Log input values. */
|
||||
for (const bNodeSocket *input_socket : this->node().input_sockets()) {
|
||||
if (!is_socket_available(input_socket)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Result &input = this->get_input(input_socket->identifier);
|
||||
if (!input_socket->is_logically_linked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input.is_single_value()) {
|
||||
tree_logger.log_value(this->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, input)});
|
||||
}
|
||||
|
||||
ImBuf *preview = compositor::compute_preview(this->context(), *result);
|
||||
nodes::eval_log::NodeTreeLogger &tree_logger =
|
||||
this->context().nodes_evaluation_log()->get_local_tree_logger(this->get_compute_context());
|
||||
tree_logger.node_image_previews.append(*tree_logger.allocator, {node_.identifier, preview});
|
||||
/* Log output values. */
|
||||
for (const bNodeSocket *output_socket : this->node().output_sockets()) {
|
||||
if (!is_socket_available(output_socket)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Result &result = this->get_result(output_socket->identifier);
|
||||
if (!result.is_allocated()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.is_single_value()) {
|
||||
tree_logger.log_value(this->node(), *output_socket, result.single_value());
|
||||
continue;
|
||||
}
|
||||
|
||||
tree_logger.output_socket_values.append(*tree_logger.allocator,
|
||||
{node_.identifier,
|
||||
output_socket->index(),
|
||||
get_image_info_log(tree_logger.allocator, result)});
|
||||
}
|
||||
|
||||
/* Log node preview. */
|
||||
if (needs_node_previews_ && is_node_preview_needed(this->node())) {
|
||||
const Result *result = this->get_preview_result();
|
||||
if (result && !result->is_single_value()) {
|
||||
ImBuf *preview = compositor::compute_preview(this->context(), *result);
|
||||
tree_logger.node_image_previews.append(*tree_logger.allocator, {node_.identifier, preview});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bNode &NodeOperation::node() const
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ void Operation::evaluate()
|
|||
{
|
||||
this->evaluate_input_processors();
|
||||
this->execute();
|
||||
this->compute_preview();
|
||||
this->log_data();
|
||||
this->release_inputs();
|
||||
this->context().evaluate_operation_post();
|
||||
}
|
||||
|
|
@ -112,7 +112,7 @@ void Operation::evaluate_input_processors()
|
|||
}
|
||||
}
|
||||
|
||||
void Operation::compute_preview() {};
|
||||
void Operation::log_data() {};
|
||||
|
||||
void Operation::populate_result(StringRef identifier, Result result)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ PixelOperation::PixelOperation(Context &context,
|
|||
{
|
||||
}
|
||||
|
||||
void PixelOperation::compute_preview()
|
||||
void PixelOperation::log_data()
|
||||
{
|
||||
for (const bNodeSocket *output : preview_outputs_) {
|
||||
Result &result = this->get_result(get_output_identifier_from_output_socket(*output));
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
#include "BLI_math_quaternion_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
|
@ -1169,4 +1171,17 @@ void Result::allocate_data(const int2 size,
|
|||
}
|
||||
}
|
||||
|
||||
StringRefNull to_string(const ResultPrecision &precision)
|
||||
{
|
||||
switch (precision) {
|
||||
case ResultPrecision::Full:
|
||||
return N_("Full");
|
||||
case ResultPrecision::Half:
|
||||
return N_("Half");
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return "None";
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@
|
|||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
#include "BLI_math_angle_types.hh"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_euler.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
|
@ -352,6 +356,9 @@ class SocketTooltipBuilder {
|
|||
else if (const auto *list_log = dynamic_cast<const eval_log::ListInfoLog *>(&value_log)) {
|
||||
this->build_tooltip_value_list_log(*list_log);
|
||||
}
|
||||
else if (const auto *image_log = dynamic_cast<const eval_log::ImageInfoLog *>(&value_log)) {
|
||||
this->build_tooltip_value_image_log(*image_log);
|
||||
}
|
||||
}
|
||||
|
||||
void build_tooltip_value_and_type_oneline(const StringRef value, const StringRef type)
|
||||
|
|
@ -773,6 +780,76 @@ class SocketTooltipBuilder {
|
|||
this->add_text_field_mono(TIP_("Type: List"));
|
||||
}
|
||||
|
||||
void build_tooltip_value_image_log(const eval_log::ImageInfoLog &image_log)
|
||||
{
|
||||
const bool has_display_window = image_log.data_size != image_log.display_size ||
|
||||
image_log.data_offset != int2(0);
|
||||
if (has_display_window) {
|
||||
this->add_text_field_mono(TIP_("Display/Data Window:"));
|
||||
this->add_text_field_mono(fmt::format(
|
||||
"\u2022 {}: {}x{}", TIP_("Data Size"), image_log.data_size.x, image_log.data_size.y));
|
||||
this->add_text_field_mono(fmt::format("\u2022 {}: {}x{}",
|
||||
TIP_("Display Size"),
|
||||
image_log.display_size.x,
|
||||
image_log.display_size.y));
|
||||
this->add_text_field_mono(fmt::format("\u2022 {}: ({}, {})",
|
||||
TIP_("Data Offset"),
|
||||
image_log.data_offset.x,
|
||||
image_log.data_offset.y));
|
||||
}
|
||||
else {
|
||||
this->add_text_field_mono(fmt::format(
|
||||
"{}: {}x{}", TIP_("Resolution"), image_log.data_size.x, image_log.data_size.y));
|
||||
}
|
||||
|
||||
this->add_space();
|
||||
|
||||
float2 location;
|
||||
math::AngleRadian rotation;
|
||||
float2 scale;
|
||||
to_loc_rot_scale(image_log.transformation, location, rotation, scale);
|
||||
|
||||
const bool is_translated = !math::is_equal(location, float2(0.0f), 10e-6f);
|
||||
const bool is_rotated = math::abs(rotation.degree()) > 10e-6f;
|
||||
const bool is_scaled = !math::is_equal(scale, float2(1.0f), 10e-6f);
|
||||
if (is_translated || is_rotated || is_scaled) {
|
||||
this->add_text_field_mono(TIP_("Transformation:"));
|
||||
if (is_translated) {
|
||||
this->add_text_field_mono(
|
||||
fmt::format("\u2022 {}: ({}, {})", TIP_("Translation"), location.x, location.y));
|
||||
}
|
||||
if (is_rotated) {
|
||||
this->add_text_field_mono(fmt::format(
|
||||
"\u2022 {}: {}" BLI_STR_UTF8_DEGREE_SIGN, TIP_("Rotation"), rotation.degree()));
|
||||
}
|
||||
if (is_scaled) {
|
||||
this->add_text_field_mono(
|
||||
fmt::format("\u2022 {}: ({}, {})", TIP_("Scale"), scale.x, scale.y));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->add_text_field_mono(TIP_("Transformation: Identity"));
|
||||
}
|
||||
|
||||
this->add_space();
|
||||
|
||||
this->add_text_field_mono(TIP_("Sampling:"));
|
||||
this->add_text_field_mono(
|
||||
fmt::format("\u2022 {}: {}", TIP_("Interpolation"), TIP_(image_log.interpolation)));
|
||||
this->add_text_field_mono(
|
||||
fmt::format("\u2022 {}: {}", TIP_("Extension X"), TIP_(image_log.extension_x)));
|
||||
this->add_text_field_mono(
|
||||
fmt::format("\u2022 {}: {}", TIP_("Extension Y"), TIP_(image_log.extension_y)));
|
||||
|
||||
this->add_space();
|
||||
|
||||
this->add_text_field_mono(fmt::format("{}: {}", TIP_("Precision"), TIP_(image_log.precision)));
|
||||
|
||||
this->add_space();
|
||||
|
||||
this->add_text_field_mono(TIP_("Type: Image"));
|
||||
}
|
||||
|
||||
void build_tooltip_value_implicit_default(const NodeDefaultInputType &type)
|
||||
{
|
||||
switch (type) {
|
||||
|
|
|
|||
|
|
@ -274,6 +274,33 @@ class ViewerNodeLog {
|
|||
const bke::GeometrySet *main_geometry() const;
|
||||
};
|
||||
|
||||
/* Compositor image result. */
|
||||
class ImageInfoLog : public ValueLog {
|
||||
public:
|
||||
/* Stores compositor::Domain information. */
|
||||
const int2 data_size;
|
||||
const int2 display_size;
|
||||
const int2 data_offset;
|
||||
const float3x3 transformation;
|
||||
|
||||
/* Stores compositor::RealizationOptions information in a textual representation. */
|
||||
const StringRefNull interpolation;
|
||||
const StringRefNull extension_x;
|
||||
const StringRefNull extension_y;
|
||||
|
||||
/* Stores compositor::Result.precision in a textual representation. */
|
||||
const StringRefNull precision;
|
||||
|
||||
ImageInfoLog(int2 data_size,
|
||||
int2 display_size,
|
||||
int2 data_offset,
|
||||
float3x3 transformation,
|
||||
StringRefNull interpolation,
|
||||
StringRefNull extension_x,
|
||||
StringRefNull extension_y,
|
||||
StringRefNull precision);
|
||||
};
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
|
||||
|
|
|
|||
|
|
@ -275,6 +275,25 @@ NodeWarning::NodeWarning(const Report &report)
|
|||
this->message = report.message;
|
||||
}
|
||||
|
||||
ImageInfoLog::ImageInfoLog(const int2 data_size,
|
||||
const int2 display_size,
|
||||
const int2 data_offset,
|
||||
const float3x3 transformation,
|
||||
const StringRefNull interpolation,
|
||||
const StringRefNull extension_x,
|
||||
const StringRefNull extension_y,
|
||||
const StringRefNull precision)
|
||||
: data_size(data_size),
|
||||
display_size(display_size),
|
||||
data_offset(data_offset),
|
||||
transformation(transformation),
|
||||
interpolation(interpolation),
|
||||
extension_x(extension_x),
|
||||
extension_y(extension_y),
|
||||
precision(precision)
|
||||
{
|
||||
}
|
||||
|
||||
/* Avoid generating these in every translation unit. */
|
||||
NodesEvalLog::NodesEvalLog() = default;
|
||||
NodesEvalLog::~NodesEvalLog() = default;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue