Fix Cycles viewport render not updating when tweaking displacement shader.
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Wed, 24 Jan 2018 19:19:48 +0000 (20:19 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Mon, 29 Jan 2018 16:07:08 +0000 (17:07 +0100)
This was disabled to avoid updating the geometry every time when the
material includes displacement, because there was no way to distinguish
between surface shader and displacement updates.

As a solution, we now compute an MD5 hash of the nodes linked to the
displacement socket, and only update the mesh if that changes.

Differential Revision: https://developer.blender.org/D3018

intern/cycles/blender/blender_mesh.cpp
intern/cycles/graph/node.cpp
intern/cycles/graph/node.h
intern/cycles/render/film.cpp
intern/cycles/render/graph.cpp
intern/cycles/render/graph.h
intern/cycles/render/mesh.cpp
intern/cycles/render/shader.cpp
intern/cycles/render/shader.h
intern/cycles/util/util_md5.cpp
intern/cycles/util/util_md5.h

index 3b07464cd960f1739b6b2034588525e2f78a2f11..cda9fb59e498ae9746537f628f718513810d512d 100644 (file)
@@ -1123,7 +1123,7 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob,
                        bool attribute_recalc = false;
 
                        foreach(Shader *shader, mesh->used_shaders)
-                               if(shader->need_update_attributes)
+                               if(shader->need_update_mesh)
                                        attribute_recalc = true;
 
                        if(!attribute_recalc)
index 10d91a1e4ef88b94f925c42ead1a38df7bd2d2ff..c71221746ad95f8a14a927bef1ec3588d9bcf10d 100644 (file)
@@ -18,6 +18,7 @@
 #include "graph/node_type.h"
 
 #include "util/util_foreach.h"
+#include "util/util_md5.h"
 #include "util/util_param.h"
 #include "util/util_transform.h"
 
@@ -403,5 +404,24 @@ bool Node::equals(const Node& other) const
        return true;
 }
 
+/* Hash */
+
+void Node::hash(MD5Hash& md5)
+{
+       md5.append(type->name.string());
+
+       foreach(const SocketType& socket, type->inputs) {
+               md5.append(socket.name.string());
+
+               if(socket.is_array()) {
+                       const array<bool>* a = (const array<bool>*)(((char*)this) + socket.struct_offset);
+                       md5.append((uint8_t*)a->data(), socket.size() * a->size());
+               }
+               else {
+                       md5.append(((uint8_t*)this) + socket.struct_offset, socket.size());
+               }
+       }
+}
+
 CCL_NAMESPACE_END
 
index 53425f5faf1f925e7da622906576957d5e0723d7..d198c38be32aefcb6819effe63194b2bad34d906 100644 (file)
@@ -24,6 +24,7 @@
 
 CCL_NAMESPACE_BEGIN
 
+class MD5Hash;
 struct Node;
 struct NodeType;
 struct Transform;
@@ -88,6 +89,9 @@ struct Node
        /* equals */
        bool equals(const Node& other) const;
 
+       /* compute hash of node and its socket values */
+       void hash(MD5Hash& md5);
+
        ustring name;
        const NodeType *type;
 };
index b305f01095f4523e83ad4416b202bc1aea0b9b41..69828cc78da6c314489a9a17897283ecde8912ef 100644 (file)
@@ -496,7 +496,7 @@ void Film::tag_passes_update(Scene *scene, const array<Pass>& passes_)
                scene->mesh_manager->tag_update(scene);
 
                foreach(Shader *shader, scene->shaders)
-                       shader->need_update_attributes = true;
+                       shader->need_update_mesh = true;
        }
        else if(Pass::contains(passes, PASS_MOTION) != Pass::contains(passes_, PASS_MOTION))
                scene->mesh_manager->tag_update(scene);
index fb2e34c2fc70e834d8a69413f512739f27ffb679..096de878e514f3807e5c772d57d393373c94b6cf 100644 (file)
@@ -23,8 +23,9 @@
 
 #include "util/util_algorithm.h"
 #include "util/util_foreach.h"
-#include "util/util_queue.h"
 #include "util/util_logging.h"
+#include "util/util_md5.h"
+#include "util/util_queue.h"
 
 CCL_NAMESPACE_BEGIN
 
@@ -683,6 +684,32 @@ void ShaderGraph::break_cycles(ShaderNode *node, vector<bool>& visited, vector<b
        on_stack[node->id] = false;
 }
 
+void ShaderGraph::compute_displacement_hash()
+{
+       /* Compute hash of all nodes linked to displacement, to detect if we need
+        * to recompute displacement when shader nodes change. */
+       ShaderInput *displacement_in = output()->input("Displacement");
+
+       if(!displacement_in->link) {
+               displacement_hash = "";
+               return;
+       }
+
+       ShaderNodeSet nodes_displace;
+       find_dependencies(nodes_displace, displacement_in);
+
+       MD5Hash md5;
+       foreach(ShaderNode *node, nodes_displace) {
+               node->hash(md5);
+               foreach(ShaderInput *input, node->inputs) {
+                       int link_id = (input->link) ? input->link->parent->id : 0;
+                       md5.append((uint8_t*)&link_id, sizeof(link_id));
+               }
+       }
+
+       displacement_hash = md5.get_hex();
+}
+
 void ShaderGraph::clean(Scene *scene)
 {
        /* Graph simplification */
index 1d1701b30a29d9e505ba86e0c6d4c15faa8f6eb5..7ed292b5b960fb963c638a10eee6bd309f75dc43 100644 (file)
@@ -42,6 +42,7 @@ class SVMCompiler;
 class OSLCompiler;
 class OutputNode;
 class ConstantFolder;
+class MD5Hash;
 
 /* Bump
  *
@@ -243,6 +244,7 @@ public:
        size_t num_node_ids;
        bool finalized;
        bool simplified;
+       string displacement_hash;
 
        ShaderGraph();
        ~ShaderGraph();
@@ -256,6 +258,7 @@ public:
        void relink(ShaderNode *node, ShaderOutput *from, ShaderOutput *to);
 
        void remove_proxy_nodes();
+       void compute_displacement_hash();
        void simplify(Scene *scene);
        void finalize(Scene *scene,
                      bool do_bump = false,
index 4bf5b60a73766e01d7faefe6dd1d68745e9751fd..5bcb47deb6526b8b7e9ba4a5ef9623d225f69d42 100644 (file)
@@ -1964,7 +1964,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
        /* Update normals. */
        foreach(Mesh *mesh, scene->meshes) {
                foreach(Shader *shader, mesh->used_shaders) {
-                       if(shader->need_update_attributes)
+                       if(shader->need_update_mesh)
                                mesh->need_update = true;
                }
 
@@ -2104,7 +2104,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
                << summary.full_report();
 
        foreach(Shader *shader, scene->shaders) {
-               shader->need_update_attributes = false;
+               shader->need_update_mesh = false;
        }
 
        Scene::MotionType need_motion = scene->need_motion();
index abb9e19a0746874f5a92d6b89886c38d78b015f4..51b7f76b9d556b4c41c6e9644283dade5ab1e1e8 100644 (file)
@@ -200,7 +200,7 @@ Shader::Shader()
        used = false;
 
        need_update = true;
-       need_update_attributes = true;
+       need_update_mesh = true;
 }
 
 Shader::~Shader()
@@ -235,9 +235,24 @@ void Shader::set_graph(ShaderGraph *graph_)
        /* do this here already so that we can detect if mesh or object attributes
         * are needed, since the node attribute callbacks check if their sockets
         * are connected but proxy nodes should not count */
-       if(graph_)
+       if(graph_) {
                graph_->remove_proxy_nodes();
 
+               if(displacement_method != DISPLACE_BUMP) {
+                       graph_->compute_displacement_hash();
+               }
+       }
+
+       /* update geometry if displacement changed */
+       if(displacement_method != DISPLACE_BUMP) {
+               const char *old_hash = (graph)? graph->displacement_hash.c_str() : "";
+               const char *new_hash = (graph_)? graph_->displacement_hash.c_str() : "";
+
+               if(strcmp(old_hash, new_hash) != 0) {
+                       need_update_mesh = true;
+               }
+       }
+
        /* assign graph */
        delete graph;
        graph = graph_;
@@ -294,9 +309,9 @@ void Shader::tag_update(Scene *scene)
        }
        
        /* compare if the attributes changed, mesh manager will check
-        * need_update_attributes, update the relevant meshes and clear it. */
+        * need_update_mesh, update the relevant meshes and clear it. */
        if(attributes.modified(prev_attributes)) {
-               need_update_attributes = true;
+               need_update_mesh = true;
                scene->mesh_manager->need_update = true;
        }
 
index 3fdcd3c0c5be356d7f4065b1901b93999e1e2aea..4a48c1347da9fa622b9ea0f0826de3c14b3eda1f 100644 (file)
@@ -98,7 +98,7 @@ public:
 
        /* synchronization */
        bool need_update;
-       bool need_update_attributes;
+       bool need_update_mesh;
 
        /* If the shader has only volume components, the surface is assumed to
         * be transparent.
index 19168135f0110407103bc97b6f689eb612b5add0..749760d84f05118c2ac16b9293d4b68a770c8702 100644 (file)
@@ -310,6 +310,13 @@ void MD5Hash::append(const uint8_t *data, int nbytes)
                memcpy(buf, p, left);
 }
 
+void MD5Hash::append(const string& str)
+{
+       if(str.size()) {
+               append((const uint8_t*)str.c_str(), str.size());
+       }
+}
+
 bool MD5Hash::append_file(const string& filepath)
 {
        FILE *f = path_fopen(filepath, "rb");
index e4cd66c85b0ee46477d69446dd7026e37e45dfa8..b043b591e674b29c550798c4ba9cd0a20bf66c5e 100644 (file)
@@ -41,6 +41,7 @@ public:
        ~MD5Hash();
 
        void append(const uint8_t *data, int size);
+       void append(const string& str);
        bool append_file(const string& filepath);
        string get_hex();