Blender  V3.3
constant_fold.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2011-2022 Blender Foundation */
3 
4 #include "scene/constant_fold.h"
5 #include "scene/shader_graph.h"
6 
7 #include "util/foreach.h"
8 #include "util/log.h"
9 
11 
15  Scene *scene)
17 {
18 }
19 
21 {
22  foreach (ShaderInput *input, node->inputs) {
23  if (input->link) {
24  return false;
25  }
26  }
27 
28  return true;
29 }
30 
31 void ConstantFolder::make_constant(float value) const
32 {
33  VLOG_DEBUG << "Folding " << node->name << "::" << output->name() << " to constant (" << value
34  << ").";
35 
36  foreach (ShaderInput *sock, output->links) {
37  sock->set(value);
38  sock->constant_folded_in = true;
39  }
40 
42 }
43 
45 {
46  VLOG_DEBUG << "Folding " << node->name << "::" << output->name() << " to constant " << value
47  << ".";
48 
49  foreach (ShaderInput *sock, output->links) {
50  sock->set(value);
51  sock->constant_folded_in = true;
52  }
53 
55 }
56 
57 void ConstantFolder::make_constant_clamp(float value, bool clamp) const
58 {
59  make_constant(clamp ? saturatef(value) : value);
60 }
61 
63 {
64  if (clamp) {
65  value.x = saturatef(value.x);
66  value.y = saturatef(value.y);
67  value.z = saturatef(value.z);
68  }
69 
70  make_constant(value);
71 }
72 
74 {
75  if (output->type() == SocketType::FLOAT) {
76  make_constant(0.0f);
77  }
78  else if (SocketType::is_float3(output->type())) {
80  }
81  else {
82  assert(0);
83  }
84 }
85 
87 {
88  if (output->type() == SocketType::FLOAT) {
89  make_constant(1.0f);
90  }
91  else if (SocketType::is_float3(output->type())) {
93  }
94  else {
95  assert(0);
96  }
97 }
98 
99 void ConstantFolder::bypass(ShaderOutput *new_output) const
100 {
101  assert(new_output);
102 
103  VLOG_DEBUG << "Folding " << node->name << "::" << output->name() << " to socket "
104  << new_output->parent->name << "::" << new_output->name() << ".";
105 
106  /* Remove all outgoing links from socket and connect them to new_output instead.
107  * The graph->relink method affects node inputs, so it's not safe to use in constant
108  * folding if the node has multiple outputs and will thus be folded multiple times. */
110 
112 
113  foreach (ShaderInput *sock, outputs) {
114  graph->connect(new_output, sock);
115  }
116 }
117 
119 {
120  assert(output->type() == SocketType::CLOSURE);
121 
122  VLOG_DEBUG << "Discarding closure " << node->name << ".";
123 
125 }
126 
128 {
129  assert(input->type() == SocketType::CLOSURE);
130 
131  if (input->link) {
132  bypass(input->link);
133  }
134  else {
135  discard();
136  }
137 }
138 
140 {
141  if (input->type() != output->type()) {
142  return false;
143  }
144  else if (!input->link) {
145  if (input->type() == SocketType::FLOAT) {
146  make_constant_clamp(node->get_float(input->socket_type), clamp);
147  return true;
148  }
149  else if (SocketType::is_float3(input->type())) {
150  make_constant_clamp(node->get_float3(input->socket_type), clamp);
151  return true;
152  }
153  }
154  else if (!clamp) {
155  bypass(input->link);
156  return true;
157  }
158  else {
159  /* disconnect other inputs if we can't fully bypass due to clamp */
160  foreach (ShaderInput *other, node->inputs) {
161  if (other != input && other->link) {
162  graph->disconnect(other);
163  }
164  }
165  }
166 
167  return false;
168 }
169 
171 {
172  if (!input->link) {
173  if (input->type() == SocketType::FLOAT) {
174  return node->get_float(input->socket_type) == 0.0f;
175  }
176  else if (SocketType::is_float3(input->type())) {
177  return node->get_float3(input->socket_type) == zero_float3();
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) == 1.0f;
189  }
190  else if (SocketType::is_float3(input->type())) {
191  return node->get_float3(input->socket_type) == one_float3();
192  }
193  }
194 
195  return false;
196 }
197 
198 /* Specific nodes */
199 
201 {
202  ShaderInput *fac_in = node->input("Fac");
203  ShaderInput *color1_in = node->input("Color1");
204  ShaderInput *color2_in = node->input("Color2");
205 
206  float fac = saturatef(node->get_float(fac_in->socket_type));
207  bool fac_is_zero = !fac_in->link && fac == 0.0f;
208  bool fac_is_one = !fac_in->link && fac == 1.0f;
209 
210  /* remove no-op node when factor is 0.0 */
211  if (fac_is_zero) {
212  /* note that some of the modes will clamp out of bounds values even without use_clamp */
213  if (!(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN)) {
214  if (try_bypass_or_make_constant(color1_in, clamp)) {
215  return;
216  }
217  }
218  }
219 
220  switch (type) {
221  case NODE_MIX_BLEND:
222  /* remove useless mix colors nodes */
223  if (color1_in->link && color2_in->link) {
224  if (color1_in->link == color2_in->link) {
226  break;
227  }
228  }
229  else if (!color1_in->link && !color2_in->link) {
230  float3 color1 = node->get_float3(color1_in->socket_type);
231  float3 color2 = node->get_float3(color2_in->socket_type);
232  if (color1 == color2) {
234  break;
235  }
236  }
237  /* remove no-op mix color node when factor is 1.0 */
238  if (fac_is_one) {
240  break;
241  }
242  break;
243  case NODE_MIX_ADD:
244  /* 0 + X (fac 1) == X */
245  if (is_zero(color1_in) && fac_is_one) {
247  }
248  /* X + 0 (fac ?) == X */
249  else if (is_zero(color2_in)) {
251  }
252  break;
253  case NODE_MIX_SUB:
254  /* X - 0 (fac ?) == X */
255  if (is_zero(color2_in)) {
257  }
258  /* X - X (fac 1) == 0 */
259  else if (color1_in->link && color1_in->link == color2_in->link && fac_is_one) {
260  make_zero();
261  }
262  break;
263  case NODE_MIX_MUL:
264  /* X * 1 (fac ?) == X, 1 * X (fac 1) == X */
265  if (is_one(color1_in) && fac_is_one) {
267  }
268  else if (is_one(color2_in)) {
270  }
271  /* 0 * ? (fac ?) == 0, ? * 0 (fac 1) == 0 */
272  else if (is_zero(color1_in)) {
273  make_zero();
274  }
275  else if (is_zero(color2_in) && fac_is_one) {
276  make_zero();
277  }
278  break;
279  case NODE_MIX_DIV:
280  /* X / 1 (fac ?) == X */
281  if (is_one(color2_in)) {
283  }
284  /* 0 / ? (fac ?) == 0 */
285  else if (is_zero(color1_in)) {
286  make_zero();
287  }
288  break;
289  default:
290  break;
291  }
292 }
293 
295 {
296  ShaderInput *value1_in = node->input("Value1");
297  ShaderInput *value2_in = node->input("Value2");
298 
299  switch (type) {
300  case NODE_MATH_ADD:
301  /* X + 0 == 0 + X == X */
302  if (is_zero(value1_in)) {
303  try_bypass_or_make_constant(value2_in);
304  }
305  else if (is_zero(value2_in)) {
306  try_bypass_or_make_constant(value1_in);
307  }
308  break;
309  case NODE_MATH_SUBTRACT:
310  /* X - 0 == X */
311  if (is_zero(value2_in)) {
312  try_bypass_or_make_constant(value1_in);
313  }
314  break;
315  case NODE_MATH_MULTIPLY:
316  /* X * 1 == 1 * X == X */
317  if (is_one(value1_in)) {
318  try_bypass_or_make_constant(value2_in);
319  }
320  else if (is_one(value2_in)) {
321  try_bypass_or_make_constant(value1_in);
322  }
323  /* X * 0 == 0 * X == 0 */
324  else if (is_zero(value1_in) || is_zero(value2_in)) {
325  make_zero();
326  }
327  break;
328  case NODE_MATH_DIVIDE:
329  /* X / 1 == X */
330  if (is_one(value2_in)) {
331  try_bypass_or_make_constant(value1_in);
332  }
333  /* 0 / X == 0 */
334  else if (is_zero(value1_in)) {
335  make_zero();
336  }
337  break;
338  case NODE_MATH_POWER:
339  /* 1 ^ X == X ^ 0 == 1 */
340  if (is_one(value1_in) || is_zero(value2_in)) {
341  make_one();
342  }
343  /* X ^ 1 == X */
344  else if (is_one(value2_in)) {
345  try_bypass_or_make_constant(value1_in);
346  }
347  default:
348  break;
349  }
350 }
351 
353 {
354  ShaderInput *vector1_in = node->input("Vector1");
355  ShaderInput *vector2_in = node->input("Vector2");
356  ShaderInput *scale_in = node->input("Scale");
357 
358  switch (type) {
360  /* X + 0 == 0 + X == X */
361  if (is_zero(vector1_in)) {
362  try_bypass_or_make_constant(vector2_in);
363  }
364  else if (is_zero(vector2_in)) {
365  try_bypass_or_make_constant(vector1_in);
366  }
367  break;
369  /* X - 0 == X */
370  if (is_zero(vector2_in)) {
371  try_bypass_or_make_constant(vector1_in);
372  }
373  break;
375  /* X * 0 == 0 * X == 0 */
376  if (is_zero(vector1_in) || is_zero(vector2_in)) {
377  make_zero();
378  } /* X * 1 == 1 * X == X */
379  else if (is_one(vector1_in)) {
380  try_bypass_or_make_constant(vector2_in);
381  }
382  else if (is_one(vector2_in)) {
383  try_bypass_or_make_constant(vector1_in);
384  }
385  break;
387  /* X / 0 == 0 / X == 0 */
388  if (is_zero(vector1_in) || is_zero(vector2_in)) {
389  make_zero();
390  } /* X / 1 == X */
391  else if (is_one(vector2_in)) {
392  try_bypass_or_make_constant(vector1_in);
393  }
394  break;
397  /* X * 0 == 0 * X == 0 */
398  if (is_zero(vector1_in) || is_zero(vector2_in)) {
399  make_zero();
400  }
401  break;
404  if (is_zero(vector1_in)) {
405  make_zero();
406  }
407  break;
409  /* X * 0 == 0 * X == 0 */
410  if (is_zero(vector1_in) || is_zero(scale_in)) {
411  make_zero();
412  } /* X * 1 == X */
413  else if (is_one(scale_in)) {
414  try_bypass_or_make_constant(vector1_in);
415  }
416  break;
417  default:
418  break;
419  }
420 }
421 
423 {
424  ShaderInput *vector_in = node->input("Vector");
425  ShaderInput *location_in = node->input("Location");
426  ShaderInput *rotation_in = node->input("Rotation");
427  ShaderInput *scale_in = node->input("Scale");
428 
429  if (is_zero(scale_in)) {
430  make_zero();
431  }
432  else if (
433  /* Can't constant fold since we always need to normalize the output. */
435  /* Check all use values are zero, note location is not used by vector and normal types. */
436  (is_zero(location_in) || type == NODE_MAPPING_TYPE_VECTOR ||
438  is_zero(rotation_in) && is_one(scale_in)) {
439  try_bypass_or_make_constant(vector_in);
440  }
441 }
442 
@ 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
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
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
ShaderGraph *const graph
Definition: constant_fold.h:20
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
ShaderOutput *const output
Definition: constant_fold.h:22
void fold_mix(NodeMix type, bool clamp) const
void fold_vector_math(NodeVectorMathType type) const
ShaderNode *const node
Definition: constant_fold.h:21
void disconnect(ShaderOutput *from)
void connect(ShaderOutput *from, ShaderInput *to)
void set(float f)
Definition: shader_graph.h:90
ShaderOutput * link
Definition: shader_graph.h:103
bool constant_folded_in
Definition: shader_graph.h:108
const SocketType & socket_type
Definition: shader_graph.h:101
ShaderInput * input(const char *name)
vector< ShaderInput * > inputs
Definition: shader_graph.h:214
ustring name()
Definition: shader_graph.h:122
SocketType::Type type()
Definition: shader_graph.h:126
vector< ShaderInput * > links
Definition: shader_graph.h:135
ShaderNode * parent
Definition: shader_graph.h:134
#define CCL_NAMESPACE_END
Definition: cuda/compat.h:9
OperationNode * node
Depsgraph * graph
Scene scene
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_global KernelShaderEvalInput * input
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:86
ccl_device_inline float3 one_float3()
Definition: math_float3.h:89
ccl_device_inline float3 zero_float3()
Definition: math_float3.h:80
T clamp(const T &a, const T &min, const T &max)
static bNodeSocketTemplate outputs[]
float get_float(const SocketType &input) const
Definition: graph/node.cpp:197
float3 get_float3(const SocketType &input) const
Definition: graph/node.cpp:209
ustring name
Definition: graph/node.h:174
static bool is_float3(Type type)
Definition: node_type.cpp:118
float z
float y
float x
ccl_device_inline float saturatef(float a)
Definition: util/math.h:404