Blender V4.3
constant_fold.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
7
8#include "util/foreach.h"
9#include "util/log.h"
10
12
14 ShaderNode *node,
15 ShaderOutput *output,
16 Scene *scene)
17 : graph(graph), node(node), output(output), scene(scene)
18{
19}
20
22{
23 foreach (ShaderInput *input, node->inputs) {
24 if (input->link) {
25 return false;
26 }
27 }
28
29 return true;
30}
31
32void ConstantFolder::make_constant(float value) const
33{
34 VLOG_DEBUG << "Folding " << node->name << "::" << output->name() << " to constant (" << value
35 << ").";
36
37 foreach (ShaderInput *sock, output->links) {
38 sock->set(value);
39 sock->constant_folded_in = true;
40 }
41
42 graph->disconnect(output);
43}
44
46{
47 VLOG_DEBUG << "Folding " << node->name << "::" << output->name() << " to constant " << value
48 << ".";
49
50 foreach (ShaderInput *sock, output->links) {
51 sock->set(value);
52 sock->constant_folded_in = true;
53 }
54
55 graph->disconnect(output);
56}
57
58void ConstantFolder::make_constant(int value) const
59{
60 VLOG_DEBUG << "Folding " << node->name << "::" << output->name() << " to constant (" << value
61 << ").";
62
63 foreach (ShaderInput *sock, output->links) {
64 sock->set(value);
65 sock->constant_folded_in = true;
66 }
67
68 graph->disconnect(output);
69}
70
71void ConstantFolder::make_constant_clamp(float value, bool clamp) const
72{
73 make_constant(clamp ? saturatef(value) : value);
74}
75
76void ConstantFolder::make_constant_clamp(float3 value, bool clamp) const
77{
78 if (clamp) {
79 value.x = saturatef(value.x);
80 value.y = saturatef(value.y);
81 value.z = saturatef(value.z);
82 }
83
84 make_constant(value);
85}
86
88{
89 if (output->type() == SocketType::FLOAT) {
90 make_constant(0.0f);
91 }
92 else if (SocketType::is_float3(output->type())) {
94 }
95 else {
96 assert(0);
97 }
98}
99
101{
102 if (output->type() == SocketType::FLOAT) {
103 make_constant(1.0f);
104 }
105 else if (SocketType::is_float3(output->type())) {
107 }
108 else {
109 assert(0);
110 }
111}
112
114{
115 assert(new_output);
116
117 VLOG_DEBUG << "Folding " << node->name << "::" << output->name() << " to socket "
118 << new_output->parent->name << "::" << new_output->name() << ".";
119
120 /* Remove all outgoing links from socket and connect them to new_output instead.
121 * The graph->relink method affects node inputs, so it's not safe to use in constant
122 * folding if the node has multiple outputs and will thus be folded multiple times. */
123 vector<ShaderInput *> outputs = output->links;
124
125 graph->disconnect(output);
126
127 foreach (ShaderInput *sock, outputs) {
128 graph->connect(new_output, sock);
129 }
130}
131
133{
134 assert(output->type() == SocketType::CLOSURE);
135
136 VLOG_DEBUG << "Discarding closure " << node->name << ".";
137
138 graph->disconnect(output);
139}
140
142{
143 assert(input->type() == SocketType::CLOSURE);
144
145 if (input->link) {
146 bypass(input->link);
147 }
148 else {
149 discard();
150 }
151}
152
154{
155 if (input->type() != output->type()) {
156 return false;
157 }
158 else if (!input->link) {
159 if (input->type() == SocketType::FLOAT) {
160 make_constant_clamp(node->get_float(input->socket_type), clamp);
161 return true;
162 }
163 else if (SocketType::is_float3(input->type())) {
164 make_constant_clamp(node->get_float3(input->socket_type), clamp);
165 return true;
166 }
167 }
168 else if (!clamp) {
169 bypass(input->link);
170 return true;
171 }
172 else {
173 /* disconnect other inputs if we can't fully bypass due to clamp */
174 foreach (ShaderInput *other, node->inputs) {
175 if (other != input && other->link) {
176 graph->disconnect(other);
177 }
178 }
179 }
180
181 return false;
182}
183
185{
186 if (!input->link) {
187 if (input->type() == SocketType::FLOAT) {
188 return node->get_float(input->socket_type) == 0.0f;
189 }
190 else if (SocketType::is_float3(input->type())) {
191 return node->get_float3(input->socket_type) == zero_float3();
192 }
193 }
194
195 return false;
196}
197
199{
200 if (!input->link) {
201 if (input->type() == SocketType::FLOAT) {
202 return node->get_float(input->socket_type) == 1.0f;
203 }
204 else if (SocketType::is_float3(input->type())) {
205 return node->get_float3(input->socket_type) == one_float3();
206 }
207 }
208
209 return false;
210}
211
212/* Specific nodes */
213
214void ConstantFolder::fold_mix(NodeMix type, bool clamp) const
215{
216 ShaderInput *fac_in = node->input("Fac");
217 ShaderInput *color1_in = node->input("Color1");
218 ShaderInput *color2_in = node->input("Color2");
219
220 float fac = saturatef(node->get_float(fac_in->socket_type));
221 bool fac_is_zero = !fac_in->link && fac == 0.0f;
222 bool fac_is_one = !fac_in->link && fac == 1.0f;
223
224 /* remove no-op node when factor is 0.0 */
225 if (fac_is_zero) {
226 /* note that some of the modes will clamp out of bounds values even without use_clamp */
227 if (!(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN)) {
228 if (try_bypass_or_make_constant(color1_in, clamp)) {
229 return;
230 }
231 }
232 }
233
234 switch (type) {
235 case NODE_MIX_BLEND:
236 /* remove useless mix colors nodes */
237 if (color1_in->link && color2_in->link) {
238 if (color1_in->link == color2_in->link) {
239 try_bypass_or_make_constant(color1_in, clamp);
240 break;
241 }
242 }
243 else if (!color1_in->link && !color2_in->link) {
244 float3 color1 = node->get_float3(color1_in->socket_type);
245 float3 color2 = node->get_float3(color2_in->socket_type);
246 if (color1 == color2) {
247 try_bypass_or_make_constant(color1_in, clamp);
248 break;
249 }
250 }
251 /* remove no-op mix color node when factor is 1.0 */
252 if (fac_is_one) {
253 try_bypass_or_make_constant(color2_in, clamp);
254 break;
255 }
256 break;
257 case NODE_MIX_ADD:
258 /* 0 + X (fac 1) == X */
259 if (is_zero(color1_in) && fac_is_one) {
260 try_bypass_or_make_constant(color2_in, clamp);
261 }
262 /* X + 0 (fac ?) == X */
263 else if (is_zero(color2_in)) {
264 try_bypass_or_make_constant(color1_in, clamp);
265 }
266 break;
267 case NODE_MIX_SUB:
268 /* X - 0 (fac ?) == X */
269 if (is_zero(color2_in)) {
270 try_bypass_or_make_constant(color1_in, clamp);
271 }
272 /* X - X (fac 1) == 0 */
273 else if (color1_in->link && color1_in->link == color2_in->link && fac_is_one) {
274 make_zero();
275 }
276 break;
277 case NODE_MIX_MUL:
278 /* X * 1 (fac ?) == X, 1 * X (fac 1) == X */
279 if (is_one(color1_in) && fac_is_one) {
280 try_bypass_or_make_constant(color2_in, clamp);
281 }
282 else if (is_one(color2_in)) {
283 try_bypass_or_make_constant(color1_in, clamp);
284 }
285 /* 0 * ? (fac ?) == 0, ? * 0 (fac 1) == 0 */
286 else if (is_zero(color1_in)) {
287 make_zero();
288 }
289 else if (is_zero(color2_in) && fac_is_one) {
290 make_zero();
291 }
292 break;
293 case NODE_MIX_DIV:
294 /* X / 1 (fac ?) == X */
295 if (is_one(color2_in)) {
296 try_bypass_or_make_constant(color1_in, clamp);
297 }
298 /* 0 / ? (fac ?) == 0 */
299 else if (is_zero(color1_in)) {
300 make_zero();
301 }
302 break;
303 default:
304 break;
305 }
306}
307
308void ConstantFolder::fold_mix_color(NodeMix type, bool clamp_factor, bool clamp) const
309{
310 ShaderInput *fac_in = node->input("Factor");
311 ShaderInput *color1_in = node->input("A");
312 ShaderInput *color2_in = node->input("B");
313
314 float fac = clamp_factor ? saturatef(node->get_float(fac_in->socket_type)) :
315 node->get_float(fac_in->socket_type);
316 bool fac_is_zero = !fac_in->link && fac == 0.0f;
317 bool fac_is_one = !fac_in->link && fac == 1.0f;
318
319 /* remove no-op node when factor is 0.0 */
320 if (fac_is_zero) {
321 /* note that some of the modes will clamp out of bounds values even without use_clamp */
322 if (!(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN)) {
323 if (try_bypass_or_make_constant(color1_in, clamp)) {
324 return;
325 }
326 }
327 }
328
329 switch (type) {
330 case NODE_MIX_BLEND:
331 /* remove useless mix colors nodes */
332 if (color1_in->link && color2_in->link) {
333 if (color1_in->link == color2_in->link) {
334 try_bypass_or_make_constant(color1_in, clamp);
335 break;
336 }
337 }
338 else if (!color1_in->link && !color2_in->link) {
339 float3 color1 = node->get_float3(color1_in->socket_type);
340 float3 color2 = node->get_float3(color2_in->socket_type);
341 if (color1 == color2) {
342 try_bypass_or_make_constant(color1_in, clamp);
343 break;
344 }
345 }
346 /* remove no-op mix color node when factor is 1.0 */
347 if (fac_is_one) {
348 try_bypass_or_make_constant(color2_in, clamp);
349 break;
350 }
351 break;
352 case NODE_MIX_ADD:
353 /* 0 + X (fac 1) == X */
354 if (is_zero(color1_in) && fac_is_one) {
355 try_bypass_or_make_constant(color2_in, clamp);
356 }
357 /* X + 0 (fac ?) == X */
358 else if (is_zero(color2_in)) {
359 try_bypass_or_make_constant(color1_in, clamp);
360 }
361 break;
362 case NODE_MIX_SUB:
363 /* X - 0 (fac ?) == X */
364 if (is_zero(color2_in)) {
365 try_bypass_or_make_constant(color1_in, clamp);
366 }
367 /* X - X (fac 1) == 0 */
368 else if (color1_in->link && color1_in->link == color2_in->link && fac_is_one) {
369 make_zero();
370 }
371 break;
372 case NODE_MIX_MUL:
373 /* X * 1 (fac ?) == X, 1 * X (fac 1) == X */
374 if (is_one(color1_in) && fac_is_one) {
375 try_bypass_or_make_constant(color2_in, clamp);
376 }
377 else if (is_one(color2_in)) {
378 try_bypass_or_make_constant(color1_in, clamp);
379 }
380 /* 0 * ? (fac ?) == 0, ? * 0 (fac 1) == 0 */
381 else if (is_zero(color1_in)) {
382 make_zero();
383 }
384 else if (is_zero(color2_in) && fac_is_one) {
385 make_zero();
386 }
387 break;
388 case NODE_MIX_DIV:
389 /* X / 1 (fac ?) == X */
390 if (is_one(color2_in)) {
391 try_bypass_or_make_constant(color1_in, clamp);
392 }
393 /* 0 / ? (fac ?) == 0 */
394 else if (is_zero(color1_in)) {
395 make_zero();
396 }
397 break;
398 default:
399 break;
400 }
401}
402
403void ConstantFolder::fold_mix_float(bool clamp_factor, bool clamp) const
404{
405 ShaderInput *fac_in = node->input("Factor");
406 ShaderInput *float1_in = node->input("A");
407 ShaderInput *float2_in = node->input("B");
408
409 float fac = clamp_factor ? saturatef(node->get_float(fac_in->socket_type)) :
410 node->get_float(fac_in->socket_type);
411 bool fac_is_zero = !fac_in->link && fac == 0.0f;
412 bool fac_is_one = !fac_in->link && fac == 1.0f;
413
414 /* remove no-op node when factor is 0.0 */
415 if (fac_is_zero) {
416 if (try_bypass_or_make_constant(float1_in, clamp)) {
417 return;
418 }
419 }
420
421 /* remove useless mix floats nodes */
422 if (float1_in->link && float2_in->link) {
423 if (float1_in->link == float2_in->link) {
424 try_bypass_or_make_constant(float1_in, clamp);
425 return;
426 }
427 }
428 else if (!float1_in->link && !float2_in->link) {
429 float value1 = node->get_float(float1_in->socket_type);
430 float value2 = node->get_float(float2_in->socket_type);
431 if (value1 == value2) {
432 try_bypass_or_make_constant(float1_in, clamp);
433 return;
434 }
435 }
436 /* remove no-op mix float node when factor is 1.0 */
437 if (fac_is_one) {
438 try_bypass_or_make_constant(float2_in, clamp);
439 return;
440 }
441}
442
444{
445 ShaderInput *value1_in = node->input("Value1");
446 ShaderInput *value2_in = node->input("Value2");
447
448 switch (type) {
449 case NODE_MATH_ADD:
450 /* X + 0 == 0 + X == X */
451 if (is_zero(value1_in)) {
453 }
454 else if (is_zero(value2_in)) {
456 }
457 break;
459 /* X - 0 == X */
460 if (is_zero(value2_in)) {
462 }
463 break;
465 /* X * 1 == 1 * X == X */
466 if (is_one(value1_in)) {
468 }
469 else if (is_one(value2_in)) {
471 }
472 /* X * 0 == 0 * X == 0 */
473 else if (is_zero(value1_in) || is_zero(value2_in)) {
474 make_zero();
475 }
476 break;
477 case NODE_MATH_DIVIDE:
478 /* X / 1 == X */
479 if (is_one(value2_in)) {
481 }
482 /* 0 / X == 0 */
483 else if (is_zero(value1_in)) {
484 make_zero();
485 }
486 break;
487 case NODE_MATH_POWER:
488 /* 1 ^ X == X ^ 0 == 1 */
489 if (is_one(value1_in) || is_zero(value2_in)) {
490 make_one();
491 }
492 /* X ^ 1 == X */
493 else if (is_one(value2_in)) {
495 }
496 default:
497 break;
498 }
499}
500
502{
503 ShaderInput *vector1_in = node->input("Vector1");
504 ShaderInput *vector2_in = node->input("Vector2");
505 ShaderInput *scale_in = node->input("Scale");
506
507 switch (type) {
509 /* X + 0 == 0 + X == X */
510 if (is_zero(vector1_in)) {
511 try_bypass_or_make_constant(vector2_in);
512 }
513 else if (is_zero(vector2_in)) {
514 try_bypass_or_make_constant(vector1_in);
515 }
516 break;
518 /* X - 0 == X */
519 if (is_zero(vector2_in)) {
520 try_bypass_or_make_constant(vector1_in);
521 }
522 break;
524 /* X * 0 == 0 * X == 0 */
525 if (is_zero(vector1_in) || is_zero(vector2_in)) {
526 make_zero();
527 } /* X * 1 == 1 * X == X */
528 else if (is_one(vector1_in)) {
529 try_bypass_or_make_constant(vector2_in);
530 }
531 else if (is_one(vector2_in)) {
532 try_bypass_or_make_constant(vector1_in);
533 }
534 break;
536 /* X / 0 == 0 / X == 0 */
537 if (is_zero(vector1_in) || is_zero(vector2_in)) {
538 make_zero();
539 } /* X / 1 == X */
540 else if (is_one(vector2_in)) {
541 try_bypass_or_make_constant(vector1_in);
542 }
543 break;
546 /* X * 0 == 0 * X == 0 */
547 if (is_zero(vector1_in) || is_zero(vector2_in)) {
548 make_zero();
549 }
550 break;
553 if (is_zero(vector1_in)) {
554 make_zero();
555 }
556 break;
558 /* X * 0 == 0 * X == 0 */
559 if (is_zero(vector1_in) || is_zero(scale_in)) {
560 make_zero();
561 } /* X * 1 == X */
562 else if (is_one(scale_in)) {
563 try_bypass_or_make_constant(vector1_in);
564 }
565 break;
566 default:
567 break;
568 }
569}
570
572{
573 ShaderInput *vector_in = node->input("Vector");
574 ShaderInput *location_in = node->input("Location");
575 ShaderInput *rotation_in = node->input("Rotation");
576 ShaderInput *scale_in = node->input("Scale");
577
578 if (is_zero(scale_in)) {
579 make_zero();
580 }
581 else if (
582 /* Can't constant fold since we always need to normalize the output. */
583 (type != NODE_MAPPING_TYPE_NORMAL) &&
584 /* Check all use values are zero, note location is not used by vector and normal types. */
585 (is_zero(location_in) || type == NODE_MAPPING_TYPE_VECTOR ||
586 type == NODE_MAPPING_TYPE_NORMAL) &&
587 is_zero(rotation_in) && is_one(scale_in))
588 {
590 }
591}
592
@ NODE_VECTOR_MATH_LENGTH
@ NODE_VECTOR_MATH_CROSS_PRODUCT
@ NODE_VECTOR_MATH_ADD
@ NODE_VECTOR_MATH_DOT_PRODUCT
@ NODE_VECTOR_MATH_ABSOLUTE
@ NODE_VECTOR_MATH_DIVIDE
@ NODE_VECTOR_MATH_MULTIPLY
@ NODE_VECTOR_MATH_SCALE
@ NODE_VECTOR_MATH_SUBTRACT
@ NODE_MATH_DIVIDE
@ NODE_MATH_POWER
@ NODE_MATH_ADD
@ NODE_MATH_MULTIPLY
@ NODE_MATH_SUBTRACT
@ NODE_MAPPING_TYPE_VECTOR
@ NODE_MAPPING_TYPE_NORMAL
void make_one() const
void make_constant_clamp(float value, bool clamp) const
void discard() const
void bypass(ShaderOutput *output) const
void bypass_or_discard(ShaderInput *input) const
void fold_mapping(NodeMappingType type) const
void fold_mix_float(bool clamp_factor, bool clamp) const
bool all_inputs_constant() const
ConstantFolder(ShaderGraph *graph, ShaderNode *node, ShaderOutput *output, Scene *scene)
bool try_bypass_or_make_constant(ShaderInput *input, bool clamp=false) const
void make_zero() const
bool is_zero(ShaderInput *input) const
bool is_one(ShaderInput *input) const
void fold_math(NodeMathType type) const
void make_constant(float value) const
void fold_mix(NodeMix type, bool clamp) const
void fold_vector_math(NodeVectorMathType type) const
void fold_mix_color(NodeMix type, bool clamp_factor, bool clamp) const
void set(float f)
ShaderOutput * link
bool constant_folded_in
const SocketType & socket_type
ustring name() const
ShaderNode * parent
#define CCL_NAMESPACE_END
#define saturatef(x)
NodeMathType
NodeMappingType
@ NODE_MIX_DIV
@ NODE_MIX_LIGHT
@ NODE_MIX_MUL
@ NODE_MIX_BURN
@ NODE_MIX_SUB
@ NODE_MIX_BLEND
@ NODE_MIX_DODGE
@ NODE_MIX_ADD
NodeVectorMathType
#define VLOG_DEBUG
Definition log.h:81
ccl_device_inline float3 one_float3()
Definition math_float3.h:24
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:15
ustring name
Definition graph/node.h:177
static bool is_float3(Type type)