Blender  V2.93
constant_fold.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "render/constant_fold.h"
18 #include "render/graph.h"
19 
20 #include "util/util_foreach.h"
21 #include "util/util_logging.h"
22 
24 
28  Scene *scene)
30 {
31 }
32 
34 {
35  foreach (ShaderInput *input, node->inputs) {
36  if (input->link) {
37  return false;
38  }
39  }
40 
41  return true;
42 }
43 
44 void ConstantFolder::make_constant(float value) const
45 {
46  VLOG(1) << "Folding " << node->name << "::" << output->name() << " to constant (" << value
47  << ").";
48 
49  foreach (ShaderInput *sock, output->links) {
50  sock->set(value);
51  }
52 
54 }
55 
57 {
58  VLOG(1) << "Folding " << node->name << "::" << output->name() << " to constant " << value << ".";
59 
60  foreach (ShaderInput *sock, output->links) {
61  sock->set(value);
62  }
63 
65 }
66 
67 void ConstantFolder::make_constant_clamp(float value, bool clamp) const
68 {
69  make_constant(clamp ? saturate(value) : value);
70 }
71 
73 {
74  if (clamp) {
75  value.x = saturate(value.x);
76  value.y = saturate(value.y);
77  value.z = saturate(value.z);
78  }
79 
80  make_constant(value);
81 }
82 
84 {
85  if (output->type() == SocketType::FLOAT) {
86  make_constant(0.0f);
87  }
88  else if (SocketType::is_float3(output->type())) {
90  }
91  else {
92  assert(0);
93  }
94 }
95 
97 {
98  if (output->type() == SocketType::FLOAT) {
99  make_constant(1.0f);
100  }
101  else if (SocketType::is_float3(output->type())) {
103  }
104  else {
105  assert(0);
106  }
107 }
108 
109 void ConstantFolder::bypass(ShaderOutput *new_output) const
110 {
111  assert(new_output);
112 
113  VLOG(1) << "Folding " << node->name << "::" << output->name() << " to socket "
114  << new_output->parent->name << "::" << new_output->name() << ".";
115 
116  /* Remove all outgoing links from socket and connect them to new_output instead.
117  * The graph->relink method affects node inputs, so it's not safe to use in constant
118  * folding if the node has multiple outputs and will thus be folded multiple times. */
120 
122 
123  foreach (ShaderInput *sock, outputs) {
124  graph->connect(new_output, sock);
125  }
126 }
127 
129 {
130  assert(output->type() == SocketType::CLOSURE);
131 
132  VLOG(1) << "Discarding closure " << node->name << ".";
133 
135 }
136 
138 {
139  assert(input->type() == SocketType::CLOSURE);
140 
141  if (input->link) {
142  bypass(input->link);
143  }
144  else {
145  discard();
146  }
147 }
148 
150 {
151  if (input->type() != output->type()) {
152  return false;
153  }
154  else if (!input->link) {
155  if (input->type() == SocketType::FLOAT) {
157  return true;
158  }
159  else if (SocketType::is_float3(input->type())) {
161  return true;
162  }
163  }
164  else if (!clamp) {
165  bypass(input->link);
166  return true;
167  }
168  else {
169  /* disconnect other inputs if we can't fully bypass due to clamp */
170  foreach (ShaderInput *other, node->inputs) {
171  if (other != input && other->link) {
172  graph->disconnect(other);
173  }
174  }
175  }
176 
177  return false;
178 }
179 
181 {
182  if (!input->link) {
183  if (input->type() == SocketType::FLOAT) {
184  return node->get_float(input->socket_type) == 0.0f;
185  }
186  else if (SocketType::is_float3(input->type())) {
187  return node->get_float3(input->socket_type) == zero_float3();
188  }
189  }
190 
191  return false;
192 }
193 
195 {
196  if (!input->link) {
197  if (input->type() == SocketType::FLOAT) {
198  return node->get_float(input->socket_type) == 1.0f;
199  }
200  else if (SocketType::is_float3(input->type())) {
201  return node->get_float3(input->socket_type) == one_float3();
202  }
203  }
204 
205  return false;
206 }
207 
208 /* Specific nodes */
209 
211 {
212  ShaderInput *fac_in = node->input("Fac");
213  ShaderInput *color1_in = node->input("Color1");
214  ShaderInput *color2_in = node->input("Color2");
215 
216  float fac = saturate(node->get_float(fac_in->socket_type));
217  bool fac_is_zero = !fac_in->link && fac == 0.0f;
218  bool fac_is_one = !fac_in->link && fac == 1.0f;
219 
220  /* remove no-op node when factor is 0.0 */
221  if (fac_is_zero) {
222  /* note that some of the modes will clamp out of bounds values even without use_clamp */
223  if (!(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN)) {
224  if (try_bypass_or_make_constant(color1_in, clamp)) {
225  return;
226  }
227  }
228  }
229 
230  switch (type) {
231  case NODE_MIX_BLEND:
232  /* remove useless mix colors nodes */
233  if (color1_in->link && color2_in->link) {
234  if (color1_in->link == color2_in->link) {
236  break;
237  }
238  }
239  else if (!color1_in->link && !color2_in->link) {
240  float3 color1 = node->get_float3(color1_in->socket_type);
241  float3 color2 = node->get_float3(color2_in->socket_type);
242  if (color1 == color2) {
244  break;
245  }
246  }
247  /* remove no-op mix color node when factor is 1.0 */
248  if (fac_is_one) {
250  break;
251  }
252  break;
253  case NODE_MIX_ADD:
254  /* 0 + X (fac 1) == X */
255  if (is_zero(color1_in) && fac_is_one) {
257  }
258  /* X + 0 (fac ?) == X */
259  else if (is_zero(color2_in)) {
261  }
262  break;
263  case NODE_MIX_SUB:
264  /* X - 0 (fac ?) == X */
265  if (is_zero(color2_in)) {
267  }
268  /* X - X (fac 1) == 0 */
269  else if (color1_in->link && color1_in->link == color2_in->link && fac_is_one) {
270  make_zero();
271  }
272  break;
273  case NODE_MIX_MUL:
274  /* X * 1 (fac ?) == X, 1 * X (fac 1) == X */
275  if (is_one(color1_in) && fac_is_one) {
277  }
278  else if (is_one(color2_in)) {
280  }
281  /* 0 * ? (fac ?) == 0, ? * 0 (fac 1) == 0 */
282  else if (is_zero(color1_in)) {
283  make_zero();
284  }
285  else if (is_zero(color2_in) && fac_is_one) {
286  make_zero();
287  }
288  break;
289  case NODE_MIX_DIV:
290  /* X / 1 (fac ?) == X */
291  if (is_one(color2_in)) {
293  }
294  /* 0 / ? (fac ?) == 0 */
295  else if (is_zero(color1_in)) {
296  make_zero();
297  }
298  break;
299  default:
300  break;
301  }
302 }
303 
305 {
306  ShaderInput *value1_in = node->input("Value1");
307  ShaderInput *value2_in = node->input("Value2");
308 
309  switch (type) {
310  case NODE_MATH_ADD:
311  /* X + 0 == 0 + X == X */
312  if (is_zero(value1_in)) {
313  try_bypass_or_make_constant(value2_in);
314  }
315  else if (is_zero(value2_in)) {
316  try_bypass_or_make_constant(value1_in);
317  }
318  break;
319  case NODE_MATH_SUBTRACT:
320  /* X - 0 == X */
321  if (is_zero(value2_in)) {
322  try_bypass_or_make_constant(value1_in);
323  }
324  break;
325  case NODE_MATH_MULTIPLY:
326  /* X * 1 == 1 * X == X */
327  if (is_one(value1_in)) {
328  try_bypass_or_make_constant(value2_in);
329  }
330  else if (is_one(value2_in)) {
331  try_bypass_or_make_constant(value1_in);
332  }
333  /* X * 0 == 0 * X == 0 */
334  else if (is_zero(value1_in) || is_zero(value2_in)) {
335  make_zero();
336  }
337  break;
338  case NODE_MATH_DIVIDE:
339  /* X / 1 == X */
340  if (is_one(value2_in)) {
341  try_bypass_or_make_constant(value1_in);
342  }
343  /* 0 / X == 0 */
344  else if (is_zero(value1_in)) {
345  make_zero();
346  }
347  break;
348  case NODE_MATH_POWER:
349  /* 1 ^ X == X ^ 0 == 1 */
350  if (is_one(value1_in) || is_zero(value2_in)) {
351  make_one();
352  }
353  /* X ^ 1 == X */
354  else if (is_one(value2_in)) {
355  try_bypass_or_make_constant(value1_in);
356  }
357  default:
358  break;
359  }
360 }
361 
363 {
364  ShaderInput *vector1_in = node->input("Vector1");
365  ShaderInput *vector2_in = node->input("Vector2");
366  ShaderInput *scale_in = node->input("Scale");
367 
368  switch (type) {
370  /* X + 0 == 0 + X == X */
371  if (is_zero(vector1_in)) {
372  try_bypass_or_make_constant(vector2_in);
373  }
374  else if (is_zero(vector2_in)) {
375  try_bypass_or_make_constant(vector1_in);
376  }
377  break;
379  /* X - 0 == X */
380  if (is_zero(vector2_in)) {
381  try_bypass_or_make_constant(vector1_in);
382  }
383  break;
385  /* X * 0 == 0 * X == 0 */
386  if (is_zero(vector1_in) || is_zero(vector2_in)) {
387  make_zero();
388  } /* X * 1 == 1 * X == X */
389  else if (is_one(vector1_in)) {
390  try_bypass_or_make_constant(vector2_in);
391  }
392  else if (is_one(vector2_in)) {
393  try_bypass_or_make_constant(vector1_in);
394  }
395  break;
397  /* X / 0 == 0 / X == 0 */
398  if (is_zero(vector1_in) || is_zero(vector2_in)) {
399  make_zero();
400  } /* X / 1 == X */
401  else if (is_one(vector2_in)) {
402  try_bypass_or_make_constant(vector1_in);
403  }
404  break;
407  /* X * 0 == 0 * X == 0 */
408  if (is_zero(vector1_in) || is_zero(vector2_in)) {
409  make_zero();
410  }
411  break;
414  if (is_zero(vector1_in)) {
415  make_zero();
416  }
417  break;
419  /* X * 0 == 0 * X == 0 */
420  if (is_zero(vector1_in) || is_zero(scale_in)) {
421  make_zero();
422  } /* X * 1 == X */
423  else if (is_one(scale_in)) {
424  try_bypass_or_make_constant(vector1_in);
425  }
426  break;
427  default:
428  break;
429  }
430 }
431 
433 {
434  ShaderInput *vector_in = node->input("Vector");
435  ShaderInput *location_in = node->input("Location");
436  ShaderInput *rotation_in = node->input("Rotation");
437  ShaderInput *scale_in = node->input("Scale");
438 
439  if (is_zero(scale_in)) {
440  make_zero();
441  }
442  else if ((is_zero(location_in) || type == NODE_MAPPING_TYPE_VECTOR ||
444  is_zero(rotation_in) && is_one(scale_in)) {
445  try_bypass_or_make_constant(vector_in);
446  }
447 }
448 
@ 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
#define output
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:33
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:35
void fold_mix(NodeMix type, bool clamp) const
void fold_vector_math(NodeVectorMathType type) const
ShaderNode *const node
Definition: constant_fold.h:34
void disconnect(ShaderOutput *from)
Definition: graph.cpp:302
void connect(ShaderOutput *from, ShaderInput *to)
Definition: graph.cpp:247
void set(float f)
Definition: graph.h:99
ShaderOutput * link
Definition: graph.h:112
SocketType::Type type()
Definition: graph.h:94
const SocketType & socket_type
Definition: graph.h:110
ShaderInput * input(const char *name)
Definition: graph.cpp:111
vector< ShaderInput * > inputs
Definition: graph.h:223
ustring name()
Definition: graph.h:127
SocketType::Type type()
Definition: graph.h:131
vector< ShaderInput * > links
Definition: graph.h:140
ShaderNode * parent
Definition: graph.h:139
OperationNode * node
Depsgraph * graph
Scene scene
#define CCL_NAMESPACE_END
static bNodeSocketTemplate outputs[]
float get_float(const SocketType &input) const
Definition: node.cpp:210
float3 get_float3(const SocketType &input) const
Definition: node.cpp:222
ustring name
Definition: node.h:174
static bool is_float3(Type type)
Definition: node_type.cpp:131
float z
Definition: sky_float3.h:35
float y
Definition: sky_float3.h:35
float x
Definition: sky_float3.h:35
NodeMathType
Definition: svm_types.h:271
NodeMappingType
Definition: svm_types.h:358
NodeMix
Definition: svm_types.h:249
@ NODE_MIX_DIV
Definition: svm_types.h:255
@ NODE_MIX_LIGHT
Definition: svm_types.h:258
@ NODE_MIX_MUL
Definition: svm_types.h:252
@ NODE_MIX_BURN
Definition: svm_types.h:261
@ NODE_MIX_SUB
Definition: svm_types.h:253
@ NODE_MIX_BLEND
Definition: svm_types.h:250
@ NODE_MIX_DODGE
Definition: svm_types.h:260
@ NODE_MIX_ADD
Definition: svm_types.h:251
NodeVectorMathType
Definition: svm_types.h:314
#define VLOG(severity)
Definition: util_logging.h:50
ccl_device_inline float saturate(float a)
Definition: util_math.h:315
ccl_device_inline int clamp(int a, int mn, int mx)
Definition: util_math.h:283
ccl_device_inline float3 one_float3()
ccl_device_inline float3 zero_float3()