Blender V5.0
node_composite_group_output.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_bounds_types.hh"
7
8#include "BLT_translation.hh"
9
10#include "UI_resources.hh"
11
12#include "DNA_space_types.h"
13
14#include "GPU_shader.hh"
15
16#include "BKE_context.hh"
17
18#include "NOD_composite.hh"
20
21#include "COM_node_operation.hh"
22#include "COM_utilities.hh"
23
25
26using namespace blender::compositor;
27
29 public:
31 {
32 for (const bNodeSocket *input : node->input_sockets()) {
33 if (!is_socket_available(input)) {
34 continue;
35 }
36
37 /* The structure type of the inputs of Group Output nodes are inferred, so we need to
38 * manually specify this here. */
39 InputDescriptor &descriptor = this->get_input_descriptor(input->identifier);
40 descriptor.expects_single_value = false;
41 }
42 }
43
44 void execute() override
45 {
46 if (!this->context().is_valid_compositing_region()) {
47 return;
48 }
49
50 /* Get the first input to be written to the output. The rest of the inputs are ignored. Only
51 * color sockets are supported. */
52 const bNodeSocket *input_socket = this->node()->input_sockets()[0];
53 if (input_socket->type != SOCK_RGBA) {
54 return;
55 }
56
57 const Result &image = this->get_input(input_socket->identifier);
58 if (image.is_single_value()) {
59 this->execute_clear(image);
60 }
61 else {
62 this->execute_copy(image);
63 }
64 }
65
66 void execute_clear(const Result &image)
67 {
69
70 const Domain domain = this->compute_domain();
71 Result output = this->context().get_output(domain);
72 if (this->context().use_gpu()) {
74 }
75 else {
76 parallel_for(domain.size, [&](const int2 texel) { output.store_pixel(texel, color); });
77 }
78 }
79
80 void execute_copy(const Result &image)
81 {
82 if (this->context().use_gpu()) {
83 this->execute_copy_gpu(image);
84 }
85 else {
86 this->execute_copy_cpu(image);
87 }
88 }
89
90 void execute_copy_gpu(const Result &image)
91 {
92 const Domain domain = this->compute_domain();
93 Result output = this->context().get_output(domain);
94
95 gpu::Shader *shader = this->context().get_shader("compositor_write_output",
96 output.precision());
97 GPU_shader_bind(shader);
98
99 const Bounds<int2> bounds = this->context().get_compositing_region();
100 GPU_shader_uniform_2iv(shader, "lower_bound", bounds.min);
101 GPU_shader_uniform_2iv(shader, "upper_bound", bounds.max);
102
103 image.bind_as_texture(shader, "input_tx");
104
105 output.bind_as_image(shader, "output_img");
106
108
109 image.unbind_as_texture();
110 output.unbind_as_image();
112 }
113
114 void execute_copy_cpu(const Result &image)
115 {
116 const Domain domain = this->compute_domain();
117 Result output = this->context().get_output(domain);
118
119 const Bounds<int2> bounds = this->context().use_context_bounds_for_input_output() ?
120 this->context().get_compositing_region() :
121 Bounds<int2>(int2(0, 0), domain.size);
122 parallel_for(domain.size, [&](const int2 texel) {
123 const int2 output_texel = texel + bounds.min;
124 if (output_texel.x > bounds.max.x || output_texel.y > bounds.max.y) {
125 return;
126 }
127 output.store_pixel(texel + bounds.min, image.load_pixel<float4>(texel));
128 });
129 }
130
131 /* The operation domain has the same size as the compositing region without any transformations
132 * applied. */
134 {
135 if (this->context().use_context_bounds_for_input_output()) {
136 return Domain(this->context().get_compositing_region_size());
137 }
139 }
140};
141
142} // namespace blender::nodes::node_composite_group_output_cc
143
144namespace blender::nodes {
145
151
153{
154 if (parameters.tree.type != NTREE_COMPOSIT) {
155 return;
156 }
157
158 SpaceNode *space_node = CTX_wm_space_node(&parameters.C);
159 if (space_node->edittree != space_node->nodetree) {
160 return;
161 }
162
163 Span<const bNodeSocket *> group_outputs = parameters.node.input_sockets().drop_back(1);
164 if (group_outputs.is_empty()) {
166 row.text = IFACE_("No Output");
167 row.icon = ICON_ERROR;
168 row.tooltip = TIP_("Node group must have a Color output socket");
169 parameters.rows.append(std::move(row));
170 return;
171 }
172
173 if (group_outputs[0]->type != SOCK_RGBA) {
175 row.text = IFACE_("Wrong Output Type");
176 row.icon = ICON_ERROR;
177 row.tooltip = TIP_("Node group's first output must be a color output");
178 parameters.rows.append(std::move(row));
179 return;
180 }
181
182 if (group_outputs.size() > 1) {
184 row.text = IFACE_("Ignored Outputs");
185 row.icon = ICON_WARNING_LARGE;
186 row.tooltip = TIP_("Only the first output is considered while the rest are ignored");
187 parameters.rows.append(std::move(row));
188 return;
189 }
190}
191
192} // namespace blender::nodes
SpaceNode * CTX_wm_space_node(const bContext *C)
#define TIP_(msgid)
#define IFACE_(msgid)
@ NTREE_COMPOSIT
@ SOCK_RGBA
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_unbind()
void GPU_shader_uniform_2iv(blender::gpu::Shader *sh, const char *name, const int data[2])
void GPU_texture_clear(blender::gpu::Texture *texture, eGPUDataFormat data_format, const void *data)
@ GPU_DATA_FLOAT
void append(const T &value)
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
NodeOperation(Context &context, DNode node)
virtual Domain compute_domain()
Definition operation.cc:56
void unbind_as_texture() const
Definition result.cc:511
void bind_as_texture(gpu::Shader *shader, const char *texture_name) const
Definition result.cc:487
T load_pixel(const int2 &texel) const
bool is_single_value() const
Definition result.cc:758
const T & get_single_value() const
#define input
#define output
descriptor
void compute_dispatch_threads_at_least(gpu::Shader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:196
void parallel_for(const int2 range, const Function &function)
compositor::NodeOperation * get_group_output_compositor_operation(compositor::Context &context, DNode node)
void get_compositor_group_output_extra_info(blender::nodes::NodeExtraInfoParams &parameters)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
struct bNodeTree * edittree
struct bNodeTree * nodetree
char identifier[64]
Vector< NodeExtraInfoRow > & rows
static pxr::UsdShadeInput get_input(const pxr::UsdShadeShader &usd_shader, const pxr::TfToken &input_name)