Fix Cycles viewport render not updating when tweaking displacement shader.

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
This commit is contained in:
Brecht Van Lommel 2018-01-24 20:19:48 +01:00
parent fb941679bb
commit 1eeb846e78
Notes: blender-bot 2023-02-14 06:13:33 +01:00
Referenced by issue #53979, Blender back-to-desktop on Cycles render
Referenced by issue #53962, Blender 2.79a rc gpu vega 56 rendering failed
11 changed files with 87 additions and 10 deletions

View 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)

View 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

View 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;
};

View 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);

View 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 */

View 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,

View 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();

View 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;
}

View 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.

View 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");

View 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();