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