Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles/render/graph.cpp')
-rw-r--r--intern/cycles/render/graph.cpp281
1 files changed, 258 insertions, 23 deletions
diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp
index f5ff091623b..4a9b2f1103c 100644
--- a/intern/cycles/render/graph.cpp
+++ b/intern/cycles/render/graph.cpp
@@ -22,9 +22,72 @@
#include "util_algorithm.h"
#include "util_debug.h"
#include "util_foreach.h"
+#include "util_queue.h"
CCL_NAMESPACE_BEGIN
+namespace {
+
+bool check_node_inputs_has_links(const ShaderNode *node)
+{
+ foreach(const ShaderInput *in, node->inputs) {
+ if(in->link) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool check_node_inputs_traversed(const ShaderNode *node,
+ const ShaderNodeSet& done)
+{
+ foreach(const ShaderInput *in, node->inputs) {
+ if(in->link) {
+ if(done.find(in->link->parent) == done.end()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool check_node_inputs_equals(const ShaderNode *node_a,
+ const ShaderNode *node_b)
+{
+ if(node_a->inputs.size() != node_b->inputs.size()) {
+ /* Happens with BSDF closure nodes which are currently sharing the same
+ * name for all the BSDF types, making it impossible to filter out
+ * incompatible nodes.
+ */
+ return false;
+ }
+ for(int i = 0; i < node_a->inputs.size(); ++i) {
+ ShaderInput *input_a = node_a->inputs[i],
+ *input_b = node_b->inputs[i];
+ if(input_a->link == NULL && input_b->link == NULL) {
+ /* Unconnected inputs are expected to have the same value. */
+ if(input_a->value != input_b->value) {
+ return false;
+ }
+ }
+ else if(input_a->link != NULL && input_b->link != NULL) {
+ /* Expect links are to come from the same exact socket. */
+ if(input_a->link != input_b->link) {
+ return false;
+ }
+ }
+ else {
+ /* One socket has a link and another has not, inputs can't be
+ * considered equal.
+ */
+ return false;
+ }
+ }
+ return true;
+}
+
+} /* namespace */
+
/* Input and Output */
ShaderInput::ShaderInput(ShaderNode *parent_, const char *name_, ShaderSocketType type_)
@@ -68,9 +131,10 @@ ShaderNode::~ShaderNode()
ShaderInput *ShaderNode::input(const char *name)
{
- foreach(ShaderInput *socket, inputs)
+ foreach(ShaderInput *socket, inputs) {
if(strcmp(socket->name, name) == 0)
return socket;
+ }
return NULL;
}
@@ -146,8 +210,7 @@ ShaderGraph::ShaderGraph()
ShaderGraph::~ShaderGraph()
{
- foreach(ShaderNode *node, nodes)
- delete node;
+ clear_nodes();
}
ShaderNode *ShaderGraph::add(ShaderNode *node)
@@ -168,15 +231,15 @@ ShaderGraph *ShaderGraph::copy()
ShaderGraph *newgraph = new ShaderGraph();
/* copy nodes */
- set<ShaderNode*> nodes_all;
+ ShaderNodeSet nodes_all;
foreach(ShaderNode *node, nodes)
nodes_all.insert(node);
- map<ShaderNode*, ShaderNode*> nodes_copy;
+ ShaderNodeMap nodes_copy;
copy_nodes(nodes_all, nodes_copy);
/* add nodes (in same order, so output is still first) */
- newgraph->nodes.clear();
+ newgraph->clear_nodes();
foreach(ShaderNode *node, nodes)
newgraph->add(nodes_copy[node]);
@@ -242,7 +305,10 @@ void ShaderGraph::relink(vector<ShaderInput*> inputs, vector<ShaderInput*> outpu
}
}
-void ShaderGraph::finalize(bool do_bump, bool do_osl)
+void ShaderGraph::finalize(Scene *scene,
+ bool do_bump,
+ bool do_osl,
+ bool do_simplify)
{
/* before compiling, the shader graph may undergo a number of modifications.
* currently we set default geometry shader inputs, and create automatic bump
@@ -250,7 +316,7 @@ void ShaderGraph::finalize(bool do_bump, bool do_osl)
* modified afterwards. */
if(!finalized) {
- clean();
+ clean(scene);
default_inputs(do_osl);
refine_bump_nodes();
@@ -269,14 +335,17 @@ void ShaderGraph::finalize(bool do_bump, bool do_osl)
finalized = true;
}
+ else if(do_simplify) {
+ simplify_settings(scene);
+ }
}
-void ShaderGraph::find_dependencies(set<ShaderNode*>& dependencies, ShaderInput *input)
+void ShaderGraph::find_dependencies(ShaderNodeSet& dependencies, ShaderInput *input)
{
/* find all nodes that this input depends on directly and indirectly */
ShaderNode *node = (input->link)? input->link->parent: NULL;
- if(node) {
+ if(node != NULL && dependencies.find(node) == dependencies.end()) {
foreach(ShaderInput *in, node->inputs)
find_dependencies(dependencies, in);
@@ -284,7 +353,15 @@ void ShaderGraph::find_dependencies(set<ShaderNode*>& dependencies, ShaderInput
}
}
-void ShaderGraph::copy_nodes(set<ShaderNode*>& nodes, map<ShaderNode*, ShaderNode*>& nnodemap)
+void ShaderGraph::clear_nodes()
+{
+ foreach(ShaderNode *node, nodes) {
+ delete node;
+ }
+ nodes.clear();
+}
+
+void ShaderGraph::copy_nodes(ShaderNodeSet& nodes, ShaderNodeMap& nnodemap)
{
/* copy a set of nodes, and the links between them. the assumption is
* made that all nodes that inputs are linked to are in the set too. */
@@ -331,6 +408,13 @@ void ShaderGraph::copy_nodes(set<ShaderNode*>& nodes, map<ShaderNode*, ShaderNod
}
}
+/* Graph simplification */
+/* ******************** */
+
+/* Step 1: Remove unused nodes.
+ * Remove nodes which are not needed in the graph, such as proxies,
+ * mix nodes with a factor of 0 or 1, emission shaders without contribution...
+ */
void ShaderGraph::remove_unneeded_nodes()
{
vector<bool> removed(num_node_ids, false);
@@ -397,7 +481,8 @@ void ShaderGraph::remove_unneeded_nodes()
if(bg->outputs[0]->links.size()) {
/* Black color or zero strength, remove node */
if((!bg->inputs[0]->link && bg->inputs[0]->value == make_float3(0.0f, 0.0f, 0.0f)) ||
- (!bg->inputs[1]->link && bg->inputs[1]->value.x == 0.0f)) {
+ (!bg->inputs[1]->link && bg->inputs[1]->value.x == 0.0f))
+ {
vector<ShaderInput*> inputs = bg->outputs[0]->links;
relink(bg->inputs, inputs, NULL);
@@ -412,7 +497,8 @@ void ShaderGraph::remove_unneeded_nodes()
if(em->outputs[0]->links.size()) {
/* Black color or zero strength, remove node */
if((!em->inputs[0]->link && em->inputs[0]->value == make_float3(0.0f, 0.0f, 0.0f)) ||
- (!em->inputs[1]->link && em->inputs[1]->value.x == 0.0f)) {
+ (!em->inputs[1]->link && em->inputs[1]->value.x == 0.0f))
+ {
vector<ShaderInput*> inputs = em->outputs[0]->links;
relink(em->inputs, inputs, NULL);
@@ -526,6 +612,139 @@ void ShaderGraph::remove_unneeded_nodes()
}
}
+/* Step 2: Constant folding.
+ * Try to constant fold some nodes, and pipe result directly to
+ * the input socket of connected nodes.
+ */
+void ShaderGraph::constant_fold()
+{
+ ShaderNodeSet done, scheduled;
+ queue<ShaderNode*> traverse_queue;
+
+ /* Schedule nodes which doesn't have any dependencies. */
+ foreach(ShaderNode *node, nodes) {
+ if(!check_node_inputs_has_links(node)) {
+ traverse_queue.push(node);
+ scheduled.insert(node);
+ }
+ }
+
+ while(!traverse_queue.empty()) {
+ ShaderNode *node = traverse_queue.front();
+ traverse_queue.pop();
+ done.insert(node);
+ foreach(ShaderOutput *output, node->outputs) {
+ /* Schedule node which was depending on the value,
+ * when possible. Do it before disconnect.
+ */
+ foreach(ShaderInput *input, output->links) {
+ if(scheduled.find(input->parent) != scheduled.end()) {
+ /* Node might not be optimized yet but scheduled already
+ * by other dependencies. No need to re-schedule it.
+ */
+ continue;
+ }
+ /* Schedule node if its inputs are fully done. */
+ if(check_node_inputs_traversed(input->parent, done)) {
+ traverse_queue.push(input->parent);
+ scheduled.insert(input->parent);
+ }
+ }
+ /* Optimize current node. */
+ float3 optimized_value = make_float3(0.0f, 0.0f, 0.0f);
+ if(node->constant_fold(output, &optimized_value)) {
+ /* Apply optimized value to connected sockets. */
+ vector<ShaderInput*> links(output->links);
+ foreach(ShaderInput *input, links) {
+ /* Assign value and disconnect the optimizedinput. */
+ input->value = optimized_value;
+ disconnect(input);
+ }
+ }
+ }
+ }
+}
+
+/* Step 3: Simplification. */
+void ShaderGraph::simplify_settings(Scene *scene)
+{
+ foreach(ShaderNode *node, nodes) {
+ node->simplify_settings(scene);
+ }
+}
+
+/* Step 4: Deduplicate nodes with same settings. */
+void ShaderGraph::deduplicate_nodes()
+{
+ /* NOTES:
+ * - Deduplication happens for nodes which has same exact settings and same
+ * exact input links configuration (either connected to same output or has
+ * the same exact default value).
+ * - Deduplication happens in the bottom-top manner, so we know for fact that
+ * all traversed nodes are either can not be deduplicated at all or were
+ * already deduplicated.
+ */
+
+ ShaderNodeSet scheduled;
+ map<ustring, ShaderNodeSet> done;
+ queue<ShaderNode*> traverse_queue;
+
+ /* Schedule nodes which doesn't have any dependencies. */
+ foreach(ShaderNode *node, nodes) {
+ if(!check_node_inputs_has_links(node)) {
+ traverse_queue.push(node);
+ scheduled.insert(node);
+ }
+ }
+
+ while(!traverse_queue.empty()) {
+ ShaderNode *node = traverse_queue.front();
+ traverse_queue.pop();
+ done[node->name].insert(node);
+ /* Schedule the nodes which were depending on the current node. */
+ foreach(ShaderOutput *output, node->outputs) {
+ foreach(ShaderInput *input, output->links) {
+ if(scheduled.find(input->parent) != scheduled.end()) {
+ /* Node might not be optimized yet but scheduled already
+ * by other dependencies. No need to re-schedule it.
+ */
+ continue;
+ }
+ /* Schedule node if its inputs are fully done. */
+ if(check_node_inputs_traversed(input->parent, done[input->parent->name])) {
+ traverse_queue.push(input->parent);
+ scheduled.insert(input->parent);
+ }
+ }
+ }
+ /* Try to merge this node with another one. */
+ foreach(ShaderNode *other_node, done[node->name]) {
+ if(node == other_node) {
+ /* Don't merge with self. */
+ continue;
+ }
+ if(node->name != other_node->name) {
+ /* Can only de-duplicate nodes of the same type. */
+ continue;
+ }
+ if(!check_node_inputs_equals(node, other_node)) {
+ /* Node inputs are different, can't merge them, */
+ continue;
+ }
+ if(!node->equals(other_node)) {
+ /* Node settings are different. */
+ continue;
+ }
+ /* TODO(sergey): Consider making it an utility function. */
+ for(int i = 0; i < node->outputs.size(); ++i) {
+ vector<ShaderInput*> inputs = node->outputs[i]->links;
+ relink(node->inputs, inputs, other_node->outputs[i]);
+ }
+ break;
+ }
+ }
+}
+
void ShaderGraph::break_cycles(ShaderNode *node, vector<bool>& visited, vector<bool>& on_stack)
{
visited[node->id] = true;
@@ -550,11 +769,27 @@ void ShaderGraph::break_cycles(ShaderNode *node, vector<bool>& visited, vector<b
on_stack[node->id] = false;
}
-void ShaderGraph::clean()
+void ShaderGraph::clean(Scene *scene)
{
- /* remove proxy and unnecessary nodes */
+ /* Graph simplification:
+ * 1: Remove unnecessary nodes
+ * 2: Constant folding
+ * 3: Simplification
+ * 4: De-duplication
+ */
+
+ /* 1: Remove proxy and unnecessary nodes. */
remove_unneeded_nodes();
+ /* 2: Constant folding. */
+ constant_fold();
+
+ /* 3: Simplification. */
+ simplify_settings(scene);
+
+ /* 4: De-duplication. */
+ deduplicate_nodes();
+
/* we do two things here: find cycles and break them, and remove unused
* nodes that don't feed into the output. how cycles are broken is
* undefined, they are invalid input, the important thing is to not crash */
@@ -659,14 +894,14 @@ void ShaderGraph::refine_bump_nodes()
foreach(ShaderNode *node, nodes) {
if(node->name == ustring("bump") && node->input("Height")->link) {
ShaderInput *bump_input = node->input("Height");
- set<ShaderNode*> nodes_bump;
+ ShaderNodeSet nodes_bump;
/* make 2 extra copies of the subgraph defined in Bump input */
- map<ShaderNode*, ShaderNode*> nodes_dx;
- map<ShaderNode*, ShaderNode*> nodes_dy;
+ ShaderNodeMap nodes_dx;
+ ShaderNodeMap nodes_dy;
/* find dependencies for the given input */
- find_dependencies(nodes_bump, bump_input );
+ find_dependencies(nodes_bump, bump_input);
copy_nodes(nodes_bump, nodes_dx);
copy_nodes(nodes_bump, nodes_dy);
@@ -725,13 +960,13 @@ void ShaderGraph::bump_from_displacement()
return;
/* find dependencies for the given input */
- set<ShaderNode*> nodes_displace;
+ ShaderNodeSet nodes_displace;
find_dependencies(nodes_displace, displacement_in);
/* copy nodes for 3 bump samples */
- map<ShaderNode*, ShaderNode*> nodes_center;
- map<ShaderNode*, ShaderNode*> nodes_dx;
- map<ShaderNode*, ShaderNode*> nodes_dy;
+ ShaderNodeMap nodes_center;
+ ShaderNodeMap nodes_dx;
+ ShaderNodeMap nodes_dy;
copy_nodes(nodes_displace, nodes_center);
copy_nodes(nodes_displace, nodes_dx);