Blender V4.3
node_composite_viewer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_bounds_types.hh"
11
12#include "BKE_global.hh"
13#include "BKE_image.hh"
14
15#include "RNA_access.hh"
16
17#include "UI_interface.hh"
18#include "UI_resources.hh"
19
20#include "GPU_shader.hh"
21#include "GPU_texture.hh"
22
23#include "COM_node_operation.hh"
24#include "COM_utilities.hh"
25
27
28/* **************** VIEWER ******************** */
29
31
33{
34 b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f});
35 b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f);
36}
37
38static void node_composit_init_viewer(bNodeTree * /*ntree*/, bNode *node)
39{
40 ImageUser *iuser = MEM_cnew<ImageUser>(__func__);
41 node->storage = iuser;
42 iuser->sfra = 1;
43
44 node->id = (ID *)BKE_image_ensure_viewer(G.main, IMA_TYPE_COMPOSITE, "Viewer Node");
45}
46
48{
49 uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
50}
51
52using namespace blender::realtime_compositor;
53
55 public:
57
58 void execute() override
59 {
60 /* See the compute_domain method for more information on the first condition. */
61 if (!context().use_composite_output() && !context().is_valid_compositing_region()) {
62 return;
63 }
64
65 const Result &image = get_input("Image");
66 const Result &alpha = get_input("Alpha");
67 if (image.is_single_value() && alpha.is_single_value()) {
69 }
70 else if (ignore_alpha()) {
72 }
73 else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
75 }
76 else {
78 }
79 }
80
81 /* Executes when all inputs are single values, in which case, the output texture can just be
82 * cleared to the appropriate color. */
84 {
85 const Result &image = get_input("Image");
86 const Result &alpha = get_input("Alpha");
87
88 float4 color = image.get_color_value();
89 if (ignore_alpha()) {
90 color.w = 1.0f;
91 }
92 else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
93 color.w = alpha.get_float_value();
94 }
95
96 const Domain domain = compute_domain();
98 domain, image.meta_data.is_non_color_data, image.precision());
99 if (this->context().use_gpu()) {
100 GPU_texture_clear(output, GPU_DATA_FLOAT, color);
101 }
102 else {
103 parallel_for(domain.size, [&](const int2 texel) { output.store_pixel(texel, color); });
104 }
105 }
106
107 /* Executes when the alpha channel of the image is ignored. */
109 {
110 if (context().use_gpu()) {
112 }
113 else {
115 }
116 }
117
119 {
120 const Result &image = get_input("Image");
121 const Domain domain = compute_domain();
123 domain, image.meta_data.is_non_color_data, image.precision());
124
125 GPUShader *shader = context().get_shader("compositor_write_output_opaque", output.precision());
126 GPU_shader_bind(shader);
127
128 const Bounds<int2> bounds = get_output_bounds();
129 GPU_shader_uniform_2iv(shader, "lower_bound", bounds.min);
130 GPU_shader_uniform_2iv(shader, "upper_bound", bounds.max);
131
132 image.bind_as_texture(shader, "input_tx");
133
134 output.bind_as_image(shader, "output_img");
135
136 compute_dispatch_threads_at_least(shader, domain.size);
137
138 image.unbind_as_texture();
139 output.unbind_as_image();
141 }
142
144 {
145 const Domain domain = compute_domain();
146 const Result &image = get_input("Image");
148 domain, image.meta_data.is_non_color_data, image.precision());
149
150 const Bounds<int2> bounds = get_output_bounds();
151 parallel_for(domain.size, [&](const int2 texel) {
152 const int2 output_texel = texel + bounds.min;
153 if (output_texel.x > bounds.max.x || output_texel.y > bounds.max.y) {
154 return;
155 }
156 output.store_pixel(texel + bounds.min, float4(image.load_pixel(texel).xyz(), 1.0f));
157 });
158 }
159
160 /* Executes when the image texture is written with no adjustments and can thus be copied directly
161 * to the output. */
163 {
164 if (context().use_gpu()) {
165 this->execute_copy_gpu();
166 }
167 else {
168 this->execute_copy_cpu();
169 }
170 }
171
173 {
174 const Result &image = get_input("Image");
175 const Domain domain = compute_domain();
176 Result output = context().get_viewer_output_result(
177 domain, image.meta_data.is_non_color_data, image.precision());
178
179 GPUShader *shader = context().get_shader("compositor_write_output", output.precision());
180 GPU_shader_bind(shader);
181
182 const Bounds<int2> bounds = get_output_bounds();
183 GPU_shader_uniform_2iv(shader, "lower_bound", bounds.min);
184 GPU_shader_uniform_2iv(shader, "upper_bound", bounds.max);
185
186 image.bind_as_texture(shader, "input_tx");
187
188 output.bind_as_image(shader, "output_img");
189
190 compute_dispatch_threads_at_least(shader, domain.size);
191
192 image.unbind_as_texture();
193 output.unbind_as_image();
195 }
196
198 {
199 const Domain domain = compute_domain();
200 const Result &image = get_input("Image");
201 Result output = context().get_viewer_output_result(
202 domain, image.meta_data.is_non_color_data, image.precision());
203
204 const Bounds<int2> bounds = get_output_bounds();
205 parallel_for(domain.size, [&](const int2 texel) {
206 const int2 output_texel = texel + bounds.min;
207 if (output_texel.x > bounds.max.x || output_texel.y > bounds.max.y) {
208 return;
209 }
210 output.store_pixel(texel + bounds.min, image.load_pixel(texel));
211 });
212 }
213
214 /* Executes when the alpha channel of the image is set as the value of the input alpha. */
216 {
217 if (context().use_gpu()) {
218 execute_set_alpha_gpu();
219 }
220 else {
221 execute_set_alpha_cpu();
222 }
223 }
224
226 {
227 const Result &image = get_input("Image");
228 const Domain domain = compute_domain();
229 Result output = context().get_viewer_output_result(
230 domain, image.meta_data.is_non_color_data, image.precision());
231
232 GPUShader *shader = context().get_shader("compositor_write_output_alpha", output.precision());
233 GPU_shader_bind(shader);
234
235 const Bounds<int2> bounds = get_output_bounds();
236 GPU_shader_uniform_2iv(shader, "lower_bound", bounds.min);
237 GPU_shader_uniform_2iv(shader, "upper_bound", bounds.max);
238
239 image.bind_as_texture(shader, "input_tx");
240
241 const Result &alpha = get_input("Alpha");
242 alpha.bind_as_texture(shader, "alpha_tx");
243
244 output.bind_as_image(shader, "output_img");
245
246 compute_dispatch_threads_at_least(shader, domain.size);
247
248 image.unbind_as_texture();
249 alpha.unbind_as_texture();
250 output.unbind_as_image();
252 }
253
255 {
256 const Domain domain = compute_domain();
257 const Result &image = get_input("Image");
258 const Result &alpha = get_input("Alpha");
259 Result output = context().get_viewer_output_result(
260 domain, image.meta_data.is_non_color_data, image.precision());
261
262 const Bounds<int2> bounds = get_output_bounds();
263 parallel_for(domain.size, [&](const int2 texel) {
264 const int2 output_texel = texel + bounds.min;
265 if (output_texel.x > bounds.max.x || output_texel.y > bounds.max.y) {
266 return;
267 }
268 output.store_pixel(texel + bounds.min,
269 float4(image.load_pixel(texel).xyz(), alpha.load_pixel(texel).x));
270 });
271 }
272
273 /* Returns the bounds of the area of the compositing region. If the context can use the composite
274 * output and thus has a dedicated viewer of an arbitrary size, then use the input in its
275 * entirety. Otherwise, no dedicated viewer exist so only write into the compositing region,
276 * which might be limited to a smaller region of the output texture. */
278 {
279 if (context().use_composite_output()) {
280 return Bounds<int2>(int2(0), compute_domain().size);
281 }
282
283 const rcti compositing_region = context().get_compositing_region();
284 return Bounds<int2>(int2(compositing_region.xmin, compositing_region.ymin),
285 int2(compositing_region.xmax, compositing_region.ymax));
286 }
287
288 /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
289 * alpha channel of the image is retained, but only if the alpha input is not linked. If the
290 * alpha input is linked, it the value of that input will be used as the alpha of the image. */
292 {
293 return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
294 }
295
297 {
298 /* The context can use the composite output and thus has a dedicated viewer of an arbitrary
299 * size, so use the input directly. Otherwise, no dedicated viewer exist so the input should be
300 * in the domain of the compositing region. */
301 if (context().use_composite_output()) {
302 const Domain domain = NodeOperation::compute_domain();
303 /* Fallback to the compositing region size in case of a single value domain. */
304 return domain.size == int2(1) ? Domain(context().get_compositing_region_size()) : domain;
305 }
306 else {
307 return Domain(context().get_compositing_region_size());
308 }
309 }
310};
311
313{
314 return new ViewerOperation(context, node);
315}
316
317} // namespace blender::nodes::node_composite_viewer_cc
318
320{
322
323 static blender::bke::bNodeType ntype;
324
325 cmp_node_type_base(&ntype, CMP_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT);
326 ntype.declare = file_ns::cmp_node_viewer_declare;
327 ntype.draw_buttons = file_ns::node_composit_buts_viewer;
328 ntype.initfunc = file_ns::node_composit_init_viewer;
331 ntype.get_compositor_operation = file_ns::get_compositor_operation;
332
333 ntype.no_muting = true;
334
336}
Image * BKE_image_ensure_viewer(Main *bmain, int type, const char *name)
#define NODE_CLASS_OUTPUT
Definition BKE_node.hh:405
@ IMA_TYPE_COMPOSITE
@ CMP_NODE_OUTPUT_IGNORE_ALPHA
void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2])
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_unbind()
void GPU_texture_clear(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
@ GPU_DATA_FLOAT
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
struct GPUShader GPUShader
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
virtual Result get_viewer_output_result(Domain domain, bool is_data, ResultPrecision precision)=0
GPUShader * get_shader(const char *info_name, ResultPrecision precision)
NodeOperation(Context &context, DNode node)
Result & get_input(StringRef identifier) const
Definition operation.cc:144
local_group_size(16, 16) .push_constant(Type b
#define G(x, y, z)
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static void node_composit_buts_viewer(uiLayout *layout, bContext *, PointerRNA *ptr)
static void cmp_node_viewer_declare(NodeDeclarationBuilder &b)
static void node_composit_init_viewer(bNodeTree *, bNode *node)
void parallel_for(const int2 range, FunctionRef< void(int2)> function)
Definition utilities.cc:252
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:131
void cmp_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void register_node_type_cmp_viewer()
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
Definition DNA_ID.h:413
Defines a node type.
Definition BKE_node.hh:218
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:324
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
int ymin
int ymax
int xmin
int xmax
static pxr::UsdShadeInput get_input(const pxr::UsdShadeShader &usd_shader, const pxr::TfToken &input_name)
PointerRNA * ptr
Definition wm_files.cc:4126