diff options
Diffstat (limited to 'intern/cycles/render/svm.cpp')
-rw-r--r-- | intern/cycles/render/svm.cpp | 1398 |
1 files changed, 688 insertions, 710 deletions
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index 360b2b461cf..d8e3e24f39e 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -49,107 +49,106 @@ void SVMShaderManager::device_update_shader(Scene *scene, Progress *progress, array<int4> *global_svm_nodes) { - if(progress->get_cancel()) { - return; - } - assert(shader->graph); + if (progress->get_cancel()) { + return; + } + assert(shader->graph); - array<int4> svm_nodes; - svm_nodes.push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); + array<int4> svm_nodes; + svm_nodes.push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); - SVMCompiler::Summary summary; - SVMCompiler compiler(scene->shader_manager, scene->image_manager, scene->light_manager); - compiler.background = (shader == scene->default_background); - compiler.compile(scene, shader, svm_nodes, 0, &summary); + SVMCompiler::Summary summary; + SVMCompiler compiler(scene->shader_manager, scene->image_manager, scene->light_manager); + compiler.background = (shader == scene->default_background); + compiler.compile(scene, shader, svm_nodes, 0, &summary); - VLOG(2) << "Compilation summary:\n" - << "Shader name: " << shader->name << "\n" - << summary.full_report(); + VLOG(2) << "Compilation summary:\n" + << "Shader name: " << shader->name << "\n" + << summary.full_report(); - nodes_lock_.lock(); - if(shader->use_mis && shader->has_surface_emission) { - scene->light_manager->need_update = true; - } + nodes_lock_.lock(); + if (shader->use_mis && shader->has_surface_emission) { + scene->light_manager->need_update = true; + } - /* The copy needs to be done inside the lock, if another thread resizes the array - * while memcpy is running, it'll be copying into possibly invalid/freed ram. - */ - size_t global_nodes_size = global_svm_nodes->size(); - global_svm_nodes->resize(global_nodes_size + svm_nodes.size()); + /* The copy needs to be done inside the lock, if another thread resizes the array + * while memcpy is running, it'll be copying into possibly invalid/freed ram. + */ + size_t global_nodes_size = global_svm_nodes->size(); + global_svm_nodes->resize(global_nodes_size + svm_nodes.size()); - /* Offset local SVM nodes to a global address space. */ - int4& jump_node = (*global_svm_nodes)[shader->id]; - jump_node.y = svm_nodes[0].y + global_nodes_size - 1; - jump_node.z = svm_nodes[0].z + global_nodes_size - 1; - jump_node.w = svm_nodes[0].w + global_nodes_size - 1; - /* Copy new nodes to global storage. */ - memcpy(&(*global_svm_nodes)[global_nodes_size], - &svm_nodes[1], - sizeof(int4) * (svm_nodes.size() - 1)); - nodes_lock_.unlock(); + /* Offset local SVM nodes to a global address space. */ + int4 &jump_node = (*global_svm_nodes)[shader->id]; + jump_node.y = svm_nodes[0].y + global_nodes_size - 1; + jump_node.z = svm_nodes[0].z + global_nodes_size - 1; + jump_node.w = svm_nodes[0].w + global_nodes_size - 1; + /* Copy new nodes to global storage. */ + memcpy(&(*global_svm_nodes)[global_nodes_size], + &svm_nodes[1], + sizeof(int4) * (svm_nodes.size() - 1)); + nodes_lock_.unlock(); } -void SVMShaderManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress) +void SVMShaderManager::device_update(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { - if(!need_update) - return; + if (!need_update) + return; - VLOG(1) << "Total " << scene->shaders.size() << " shaders."; + VLOG(1) << "Total " << scene->shaders.size() << " shaders."; - double start_time = time_dt(); + double start_time = time_dt(); - /* test if we need to update */ - device_free(device, dscene, scene); + /* test if we need to update */ + device_free(device, dscene, scene); - /* determine which shaders are in use */ - device_update_shaders_used(scene); + /* determine which shaders are in use */ + device_update_shaders_used(scene); - /* svm_nodes */ - array<int4> svm_nodes; - size_t i; + /* svm_nodes */ + array<int4> svm_nodes; + size_t i; - for(i = 0; i < scene->shaders.size(); i++) { - svm_nodes.push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); - } + for (i = 0; i < scene->shaders.size(); i++) { + svm_nodes.push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); + } - TaskPool task_pool; - foreach(Shader *shader, scene->shaders) { - task_pool.push(function_bind(&SVMShaderManager::device_update_shader, - this, - scene, - shader, - &progress, - &svm_nodes), - false); - } - task_pool.wait_work(); + TaskPool task_pool; + foreach (Shader *shader, scene->shaders) { + task_pool.push( + function_bind( + &SVMShaderManager::device_update_shader, this, scene, shader, &progress, &svm_nodes), + false); + } + task_pool.wait_work(); - if(progress.get_cancel()) { - return; - } + if (progress.get_cancel()) { + return; + } - dscene->svm_nodes.steal_data(svm_nodes); - dscene->svm_nodes.copy_to_device(); + dscene->svm_nodes.steal_data(svm_nodes); + dscene->svm_nodes.copy_to_device(); - for(i = 0; i < scene->shaders.size(); i++) { - Shader *shader = scene->shaders[i]; - shader->need_update = false; - } + for (i = 0; i < scene->shaders.size(); i++) { + Shader *shader = scene->shaders[i]; + shader->need_update = false; + } - device_update_common(device, dscene, scene, progress); + device_update_common(device, dscene, scene, progress); - need_update = false; + need_update = false; - VLOG(1) << "Shader manager updated " - << scene->shaders.size() << " shaders in " - << time_dt() - start_time << " seconds."; + VLOG(1) << "Shader manager updated " << scene->shaders.size() << " shaders in " + << time_dt() - start_time << " seconds."; } void SVMShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene) { - device_free_common(device, dscene, scene); + device_free_common(device, dscene, scene); - dscene->svm_nodes.free(); + dscene->svm_nodes.free(); } /* Graph Compiler */ @@ -158,772 +157,751 @@ SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, ImageManager *image_manager_, LightManager *light_manager_) { - shader_manager = shader_manager_; - image_manager = image_manager_; - light_manager = light_manager_; - max_stack_use = 0; - current_type = SHADER_TYPE_SURFACE; - current_shader = NULL; - current_graph = NULL; - background = false; - mix_weight_offset = SVM_STACK_INVALID; - compile_failed = false; + shader_manager = shader_manager_; + image_manager = image_manager_; + light_manager = light_manager_; + max_stack_use = 0; + current_type = SHADER_TYPE_SURFACE; + current_shader = NULL; + current_graph = NULL; + background = false; + mix_weight_offset = SVM_STACK_INVALID; + compile_failed = false; } int SVMCompiler::stack_size(SocketType::Type type) { - int size = 0; - - switch(type) { - case SocketType::FLOAT: - case SocketType::INT: - size = 1; - break; - case SocketType::COLOR: - case SocketType::VECTOR: - case SocketType::NORMAL: - case SocketType::POINT: - size = 3; - break; - case SocketType::CLOSURE: - size = 0; - break; - default: - assert(0); - break; - } - - return size; + int size = 0; + + switch (type) { + case SocketType::FLOAT: + case SocketType::INT: + size = 1; + break; + case SocketType::COLOR: + case SocketType::VECTOR: + case SocketType::NORMAL: + case SocketType::POINT: + size = 3; + break; + case SocketType::CLOSURE: + size = 0; + break; + default: + assert(0); + break; + } + + return size; } int SVMCompiler::stack_find_offset(int size) { - int offset = -1; + int offset = -1; - /* find free space in stack & mark as used */ - for(int i = 0, num_unused = 0; i < SVM_STACK_SIZE; i++) { - if(active_stack.users[i]) num_unused = 0; - else num_unused++; + /* find free space in stack & mark as used */ + for (int i = 0, num_unused = 0; i < SVM_STACK_SIZE; i++) { + if (active_stack.users[i]) + num_unused = 0; + else + num_unused++; - if(num_unused == size) { - offset = i+1 - size; - max_stack_use = max(i+1, max_stack_use); + if (num_unused == size) { + offset = i + 1 - size; + max_stack_use = max(i + 1, max_stack_use); - while(i >= offset) - active_stack.users[i--] = 1; + while (i >= offset) + active_stack.users[i--] = 1; - return offset; - } - } + return offset; + } + } - if(!compile_failed) { - compile_failed = true; - fprintf(stderr, "Cycles: out of SVM stack space, shader \"%s\" too big.\n", current_shader->name.c_str()); - } + if (!compile_failed) { + compile_failed = true; + fprintf(stderr, + "Cycles: out of SVM stack space, shader \"%s\" too big.\n", + current_shader->name.c_str()); + } - return 0; + return 0; } int SVMCompiler::stack_find_offset(SocketType::Type type) { - return stack_find_offset(stack_size(type)); + return stack_find_offset(stack_size(type)); } void SVMCompiler::stack_clear_offset(SocketType::Type type, int offset) { - int size = stack_size(type); + int size = stack_size(type); - for(int i = 0; i < size; i++) - active_stack.users[offset + i]--; + for (int i = 0; i < size; i++) + active_stack.users[offset + i]--; } int SVMCompiler::stack_assign(ShaderInput *input) { - /* stack offset assign? */ - if(input->stack_offset == SVM_STACK_INVALID) { - if(input->link) { - /* linked to output -> use output offset */ - assert(input->link->stack_offset != SVM_STACK_INVALID); - input->stack_offset = input->link->stack_offset; - } - else { - Node *node = input->parent; - - /* not linked to output -> add nodes to load default value */ - input->stack_offset = stack_find_offset(input->type()); - - if(input->type() == SocketType::FLOAT) { - add_node(NODE_VALUE_F, __float_as_int(node->get_float(input->socket_type)), input->stack_offset); - } - else if(input->type() == SocketType::INT) { - add_node(NODE_VALUE_F, node->get_int(input->socket_type), input->stack_offset); - } - else if(input->type() == SocketType::VECTOR || - input->type() == SocketType::NORMAL || - input->type() == SocketType::POINT || - input->type() == SocketType::COLOR) - { - - add_node(NODE_VALUE_V, input->stack_offset); - add_node(NODE_VALUE_V, node->get_float3(input->socket_type)); - } - else /* should not get called for closure */ - assert(0); - } - } - - return input->stack_offset; + /* stack offset assign? */ + if (input->stack_offset == SVM_STACK_INVALID) { + if (input->link) { + /* linked to output -> use output offset */ + assert(input->link->stack_offset != SVM_STACK_INVALID); + input->stack_offset = input->link->stack_offset; + } + else { + Node *node = input->parent; + + /* not linked to output -> add nodes to load default value */ + input->stack_offset = stack_find_offset(input->type()); + + if (input->type() == SocketType::FLOAT) { + add_node(NODE_VALUE_F, + __float_as_int(node->get_float(input->socket_type)), + input->stack_offset); + } + else if (input->type() == SocketType::INT) { + add_node(NODE_VALUE_F, node->get_int(input->socket_type), input->stack_offset); + } + else if (input->type() == SocketType::VECTOR || input->type() == SocketType::NORMAL || + input->type() == SocketType::POINT || input->type() == SocketType::COLOR) { + + add_node(NODE_VALUE_V, input->stack_offset); + add_node(NODE_VALUE_V, node->get_float3(input->socket_type)); + } + else /* should not get called for closure */ + assert(0); + } + } + + return input->stack_offset; } int SVMCompiler::stack_assign(ShaderOutput *output) { - /* if no stack offset assigned yet, find one */ - if(output->stack_offset == SVM_STACK_INVALID) - output->stack_offset = stack_find_offset(output->type()); + /* if no stack offset assigned yet, find one */ + if (output->stack_offset == SVM_STACK_INVALID) + output->stack_offset = stack_find_offset(output->type()); - return output->stack_offset; + return output->stack_offset; } int SVMCompiler::stack_assign_if_linked(ShaderInput *input) { - if(input->link) - return stack_assign(input); + if (input->link) + return stack_assign(input); - return SVM_STACK_INVALID; + return SVM_STACK_INVALID; } int SVMCompiler::stack_assign_if_linked(ShaderOutput *output) { - if(!output->links.empty()) - return stack_assign(output); + if (!output->links.empty()) + return stack_assign(output); - return SVM_STACK_INVALID; + return SVM_STACK_INVALID; } void SVMCompiler::stack_link(ShaderInput *input, ShaderOutput *output) { - if(output->stack_offset == SVM_STACK_INVALID) { - assert(input->link); - assert(stack_size(output->type()) == stack_size(input->link->type())); + if (output->stack_offset == SVM_STACK_INVALID) { + assert(input->link); + assert(stack_size(output->type()) == stack_size(input->link->type())); - output->stack_offset = input->link->stack_offset; + output->stack_offset = input->link->stack_offset; - int size = stack_size(output->type()); + int size = stack_size(output->type()); - for(int i = 0; i < size; i++) - active_stack.users[output->stack_offset + i]++; - } + for (int i = 0; i < size; i++) + active_stack.users[output->stack_offset + i]++; + } } -void SVMCompiler::stack_clear_users(ShaderNode *node, ShaderNodeSet& done) +void SVMCompiler::stack_clear_users(ShaderNode *node, ShaderNodeSet &done) { - /* optimization we should add: - * find and lower user counts for outputs for which all inputs are done. - * this is done before the node is compiled, under the assumption that the - * node will first load all inputs from the stack and then writes its - * outputs. this used to work, but was disabled because it gave trouble - * with inputs getting stack positions assigned */ + /* optimization we should add: + * find and lower user counts for outputs for which all inputs are done. + * this is done before the node is compiled, under the assumption that the + * node will first load all inputs from the stack and then writes its + * outputs. this used to work, but was disabled because it gave trouble + * with inputs getting stack positions assigned */ - foreach(ShaderInput *input, node->inputs) { - ShaderOutput *output = input->link; + foreach (ShaderInput *input, node->inputs) { + ShaderOutput *output = input->link; - if(output && output->stack_offset != SVM_STACK_INVALID) { - bool all_done = true; + if (output && output->stack_offset != SVM_STACK_INVALID) { + bool all_done = true; - /* optimization we should add: verify if in->parent is actually used */ - foreach(ShaderInput *in, output->links) - if(in->parent != node && done.find(in->parent) == done.end()) - all_done = false; + /* optimization we should add: verify if in->parent is actually used */ + foreach (ShaderInput *in, output->links) + if (in->parent != node && done.find(in->parent) == done.end()) + all_done = false; - if(all_done) { - stack_clear_offset(output->type(), output->stack_offset); - output->stack_offset = SVM_STACK_INVALID; + if (all_done) { + stack_clear_offset(output->type(), output->stack_offset); + output->stack_offset = SVM_STACK_INVALID; - foreach(ShaderInput *in, output->links) - in->stack_offset = SVM_STACK_INVALID; - } - } - } + foreach (ShaderInput *in, output->links) + in->stack_offset = SVM_STACK_INVALID; + } + } + } } void SVMCompiler::stack_clear_temporary(ShaderNode *node) { - foreach(ShaderInput *input, node->inputs) { - if(!input->link && input->stack_offset != SVM_STACK_INVALID) { - stack_clear_offset(input->type(), input->stack_offset); - input->stack_offset = SVM_STACK_INVALID; - } - } + foreach (ShaderInput *input, node->inputs) { + if (!input->link && input->stack_offset != SVM_STACK_INVALID) { + stack_clear_offset(input->type(), input->stack_offset); + input->stack_offset = SVM_STACK_INVALID; + } + } } uint SVMCompiler::encode_uchar4(uint x, uint y, uint z, uint w) { - assert(x <= 255); - assert(y <= 255); - assert(z <= 255); - assert(w <= 255); + assert(x <= 255); + assert(y <= 255); + assert(z <= 255); + assert(w <= 255); - return (x) | (y << 8) | (z << 16) | (w << 24); + return (x) | (y << 8) | (z << 16) | (w << 24); } void SVMCompiler::add_node(int a, int b, int c, int d) { - current_svm_nodes.push_back_slow(make_int4(a, b, c, d)); + current_svm_nodes.push_back_slow(make_int4(a, b, c, d)); } void SVMCompiler::add_node(ShaderNodeType type, int a, int b, int c) { - current_svm_nodes.push_back_slow(make_int4(type, a, b, c)); + current_svm_nodes.push_back_slow(make_int4(type, a, b, c)); } -void SVMCompiler::add_node(ShaderNodeType type, const float3& f) +void SVMCompiler::add_node(ShaderNodeType type, const float3 &f) { - current_svm_nodes.push_back_slow(make_int4(type, - __float_as_int(f.x), - __float_as_int(f.y), - __float_as_int(f.z))); + current_svm_nodes.push_back_slow( + make_int4(type, __float_as_int(f.x), __float_as_int(f.y), __float_as_int(f.z))); } -void SVMCompiler::add_node(const float4& f) +void SVMCompiler::add_node(const float4 &f) { - current_svm_nodes.push_back_slow(make_int4( - __float_as_int(f.x), - __float_as_int(f.y), - __float_as_int(f.z), - __float_as_int(f.w))); + current_svm_nodes.push_back_slow(make_int4( + __float_as_int(f.x), __float_as_int(f.y), __float_as_int(f.z), __float_as_int(f.w))); } uint SVMCompiler::attribute(ustring name) { - return shader_manager->get_attribute_id(name); + return shader_manager->get_attribute_id(name); } uint SVMCompiler::attribute(AttributeStandard std) { - return shader_manager->get_attribute_id(std); + return shader_manager->get_attribute_id(std); } uint SVMCompiler::attribute_standard(ustring name) { - AttributeStandard std = Attribute::name_standard(name.c_str()); - return (std)? attribute(std): attribute(name); + AttributeStandard std = Attribute::name_standard(name.c_str()); + return (std) ? attribute(std) : attribute(name); } -void SVMCompiler::find_dependencies(ShaderNodeSet& dependencies, - const ShaderNodeSet& done, +void SVMCompiler::find_dependencies(ShaderNodeSet &dependencies, + const ShaderNodeSet &done, ShaderInput *input, ShaderNode *skip_node) { - ShaderNode *node = (input->link)? input->link->parent: NULL; - if(node != NULL && - done.find(node) == done.end() && - node != skip_node && - dependencies.find(node) == dependencies.end()) - { - foreach(ShaderInput *in, node->inputs) { - find_dependencies(dependencies, done, in, skip_node); - } - dependencies.insert(node); - } -} - -void SVMCompiler::generate_node(ShaderNode *node, ShaderNodeSet& done) -{ - node->compile(*this); - stack_clear_users(node, done); - stack_clear_temporary(node); - - if(current_type == SHADER_TYPE_SURFACE) { - if(node->has_spatial_varying()) - current_shader->has_surface_spatial_varying = true; - } - else if(current_type == SHADER_TYPE_VOLUME) { - if(node->has_spatial_varying()) - current_shader->has_volume_spatial_varying = true; - } - - if(node->has_object_dependency()) { - current_shader->has_object_dependency = true; - } - - if(node->has_attribute_dependency()) { - current_shader->has_attribute_dependency = true; - } - - if(node->has_integrator_dependency()) { - current_shader->has_integrator_dependency = true; - } -} - -void SVMCompiler::generate_svm_nodes(const ShaderNodeSet& nodes, - CompilerState *state) -{ - ShaderNodeSet& done = state->nodes_done; - vector<bool>& done_flag = state->nodes_done_flag; - - bool nodes_done; - do { - nodes_done = true; - - foreach(ShaderNode *node, nodes) { - if(!done_flag[node->id]) { - bool inputs_done = true; - - foreach(ShaderInput *input, node->inputs) { - if(input->link && !done_flag[input->link->parent->id]) { - inputs_done = false; - } - } - if(inputs_done) { - generate_node(node, done); - done.insert(node); - done_flag[node->id] = true; - } - else { - nodes_done = false; - } - } - } - } while(!nodes_done); -} - -void SVMCompiler::generate_closure_node(ShaderNode *node, - CompilerState *state) -{ - /* execute dependencies for closure */ - foreach(ShaderInput *in, node->inputs) { - if(in->link != NULL) { - ShaderNodeSet dependencies; - find_dependencies(dependencies, state->nodes_done, in); - generate_svm_nodes(dependencies, state); - } - } - - /* closure mix weight */ - const char *weight_name = (current_type == SHADER_TYPE_VOLUME)? "VolumeMixWeight": "SurfaceMixWeight"; - ShaderInput *weight_in = node->input(weight_name); - - if(weight_in && (weight_in->link || node->get_float(weight_in->socket_type) != 1.0f)) - mix_weight_offset = stack_assign(weight_in); - else - mix_weight_offset = SVM_STACK_INVALID; - - /* compile closure itself */ - generate_node(node, state->nodes_done); - - mix_weight_offset = SVM_STACK_INVALID; - - if(current_type == SHADER_TYPE_SURFACE) { - if(node->has_surface_emission()) - current_shader->has_surface_emission = true; - if(node->has_surface_transparent()) - current_shader->has_surface_transparent = true; - if(node->has_surface_bssrdf()) { - current_shader->has_surface_bssrdf = true; - if(node->has_bssrdf_bump()) - current_shader->has_bssrdf_bump = true; - } - if(node->has_bump()) { - current_shader->has_bump = true; - } - } + ShaderNode *node = (input->link) ? input->link->parent : NULL; + if (node != NULL && done.find(node) == done.end() && node != skip_node && + dependencies.find(node) == dependencies.end()) { + foreach (ShaderInput *in, node->inputs) { + find_dependencies(dependencies, done, in, skip_node); + } + dependencies.insert(node); + } +} + +void SVMCompiler::generate_node(ShaderNode *node, ShaderNodeSet &done) +{ + node->compile(*this); + stack_clear_users(node, done); + stack_clear_temporary(node); + + if (current_type == SHADER_TYPE_SURFACE) { + if (node->has_spatial_varying()) + current_shader->has_surface_spatial_varying = true; + } + else if (current_type == SHADER_TYPE_VOLUME) { + if (node->has_spatial_varying()) + current_shader->has_volume_spatial_varying = true; + } + + if (node->has_object_dependency()) { + current_shader->has_object_dependency = true; + } + + if (node->has_attribute_dependency()) { + current_shader->has_attribute_dependency = true; + } + + if (node->has_integrator_dependency()) { + current_shader->has_integrator_dependency = true; + } +} + +void SVMCompiler::generate_svm_nodes(const ShaderNodeSet &nodes, CompilerState *state) +{ + ShaderNodeSet &done = state->nodes_done; + vector<bool> &done_flag = state->nodes_done_flag; + + bool nodes_done; + do { + nodes_done = true; + + foreach (ShaderNode *node, nodes) { + if (!done_flag[node->id]) { + bool inputs_done = true; + + foreach (ShaderInput *input, node->inputs) { + if (input->link && !done_flag[input->link->parent->id]) { + inputs_done = false; + } + } + if (inputs_done) { + generate_node(node, done); + done.insert(node); + done_flag[node->id] = true; + } + else { + nodes_done = false; + } + } + } + } while (!nodes_done); +} + +void SVMCompiler::generate_closure_node(ShaderNode *node, CompilerState *state) +{ + /* execute dependencies for closure */ + foreach (ShaderInput *in, node->inputs) { + if (in->link != NULL) { + ShaderNodeSet dependencies; + find_dependencies(dependencies, state->nodes_done, in); + generate_svm_nodes(dependencies, state); + } + } + + /* closure mix weight */ + const char *weight_name = (current_type == SHADER_TYPE_VOLUME) ? "VolumeMixWeight" : + "SurfaceMixWeight"; + ShaderInput *weight_in = node->input(weight_name); + + if (weight_in && (weight_in->link || node->get_float(weight_in->socket_type) != 1.0f)) + mix_weight_offset = stack_assign(weight_in); + else + mix_weight_offset = SVM_STACK_INVALID; + + /* compile closure itself */ + generate_node(node, state->nodes_done); + + mix_weight_offset = SVM_STACK_INVALID; + + if (current_type == SHADER_TYPE_SURFACE) { + if (node->has_surface_emission()) + current_shader->has_surface_emission = true; + if (node->has_surface_transparent()) + current_shader->has_surface_transparent = true; + if (node->has_surface_bssrdf()) { + current_shader->has_surface_bssrdf = true; + if (node->has_bssrdf_bump()) + current_shader->has_bssrdf_bump = true; + } + if (node->has_bump()) { + current_shader->has_bump = true; + } + } } void SVMCompiler::generated_shared_closure_nodes(ShaderNode *root_node, ShaderNode *node, CompilerState *state, - const ShaderNodeSet& shared) -{ - if(shared.find(node) != shared.end()) { - generate_multi_closure(root_node, node, state); - } - else { - foreach(ShaderInput *in, node->inputs) { - if(in->type() == SocketType::CLOSURE && in->link) - generated_shared_closure_nodes(root_node, - in->link->parent, - state, - shared); - } - } + const ShaderNodeSet &shared) +{ + if (shared.find(node) != shared.end()) { + generate_multi_closure(root_node, node, state); + } + else { + foreach (ShaderInput *in, node->inputs) { + if (in->type() == SocketType::CLOSURE && in->link) + generated_shared_closure_nodes(root_node, in->link->parent, state, shared); + } + } } void SVMCompiler::generate_multi_closure(ShaderNode *root_node, ShaderNode *node, CompilerState *state) { - /* only generate once */ - if(state->closure_done.find(node) != state->closure_done.end()) - return; - - state->closure_done.insert(node); - - if(node->special_type == SHADER_SPECIAL_TYPE_COMBINE_CLOSURE) { - /* weighting is already taken care of in ShaderGraph::transform_multi_closure */ - ShaderInput *cl1in = node->input("Closure1"); - ShaderInput *cl2in = node->input("Closure2"); - ShaderInput *facin = node->input("Fac"); - - /* skip empty mix/add closure nodes */ - if(!cl1in->link && !cl2in->link) - return; - - if(facin && facin->link) { - /* mix closure: generate instructions to compute mix weight */ - ShaderNodeSet dependencies; - find_dependencies(dependencies, state->nodes_done, facin); - generate_svm_nodes(dependencies, state); - - /* execute shared dependencies. this is needed to allow skipping - * of zero weight closures and their dependencies later, so we - * ensure that they only skip dependencies that are unique to them */ - ShaderNodeSet cl1deps, cl2deps, shareddeps; - - find_dependencies(cl1deps, state->nodes_done, cl1in); - find_dependencies(cl2deps, state->nodes_done, cl2in); - - ShaderNodeIDComparator node_id_comp; - set_intersection(cl1deps.begin(), cl1deps.end(), - cl2deps.begin(), cl2deps.end(), - std::inserter(shareddeps, shareddeps.begin()), - node_id_comp); - - /* it's possible some nodes are not shared between this mix node - * inputs, but still needed to be always executed, this mainly - * happens when a node of current subbranch is used by a parent - * node or so */ - if(root_node != node) { - foreach(ShaderInput *in, root_node->inputs) { - ShaderNodeSet rootdeps; - find_dependencies(rootdeps, state->nodes_done, in, node); - set_intersection(rootdeps.begin(), rootdeps.end(), - cl1deps.begin(), cl1deps.end(), - std::inserter(shareddeps, shareddeps.begin()), - node_id_comp); - set_intersection(rootdeps.begin(), rootdeps.end(), - cl2deps.begin(), cl2deps.end(), - std::inserter(shareddeps, shareddeps.begin()), - node_id_comp); - } - } - - if(!shareddeps.empty()) { - if(cl1in->link) { - generated_shared_closure_nodes(root_node, - cl1in->link->parent, - state, - shareddeps); - } - if(cl2in->link) { - generated_shared_closure_nodes(root_node, - cl2in->link->parent, - state, - shareddeps); - } - - generate_svm_nodes(shareddeps, state); - } - - /* generate instructions for input closure 1 */ - if(cl1in->link) { - /* Add instruction to skip closure and its dependencies if mix - * weight is zero. - */ - current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ONE, - 0, - stack_assign(facin), - 0)); - int node_jump_skip_index = current_svm_nodes.size() - 1; - - generate_multi_closure(root_node, cl1in->link->parent, state); - - /* Fill in jump instruction location to be after closure. */ - current_svm_nodes[node_jump_skip_index].y = - current_svm_nodes.size() - node_jump_skip_index - 1; - } - - /* generate instructions for input closure 2 */ - if(cl2in->link) { - /* Add instruction to skip closure and its dependencies if mix - * weight is zero. - */ - current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ZERO, - 0, - stack_assign(facin), - 0)); - int node_jump_skip_index = current_svm_nodes.size() - 1; - - generate_multi_closure(root_node, cl2in->link->parent, state); - - /* Fill in jump instruction location to be after closure. */ - current_svm_nodes[node_jump_skip_index].y = - current_svm_nodes.size() - node_jump_skip_index - 1; - } - - /* unassign */ - facin->stack_offset = SVM_STACK_INVALID; - } - else { - /* execute closures and their dependencies, no runtime checks - * to skip closures here because was already optimized due to - * fixed weight or add closure that always needs both */ - if(cl1in->link) - generate_multi_closure(root_node, cl1in->link->parent, state); - if(cl2in->link) - generate_multi_closure(root_node, cl2in->link->parent, state); - } - } - else { - generate_closure_node(node, state); - } - - state->nodes_done.insert(node); - state->nodes_done_flag[node->id] = true; + /* only generate once */ + if (state->closure_done.find(node) != state->closure_done.end()) + return; + + state->closure_done.insert(node); + + if (node->special_type == SHADER_SPECIAL_TYPE_COMBINE_CLOSURE) { + /* weighting is already taken care of in ShaderGraph::transform_multi_closure */ + ShaderInput *cl1in = node->input("Closure1"); + ShaderInput *cl2in = node->input("Closure2"); + ShaderInput *facin = node->input("Fac"); + + /* skip empty mix/add closure nodes */ + if (!cl1in->link && !cl2in->link) + return; + + if (facin && facin->link) { + /* mix closure: generate instructions to compute mix weight */ + ShaderNodeSet dependencies; + find_dependencies(dependencies, state->nodes_done, facin); + generate_svm_nodes(dependencies, state); + + /* execute shared dependencies. this is needed to allow skipping + * of zero weight closures and their dependencies later, so we + * ensure that they only skip dependencies that are unique to them */ + ShaderNodeSet cl1deps, cl2deps, shareddeps; + + find_dependencies(cl1deps, state->nodes_done, cl1in); + find_dependencies(cl2deps, state->nodes_done, cl2in); + + ShaderNodeIDComparator node_id_comp; + set_intersection(cl1deps.begin(), + cl1deps.end(), + cl2deps.begin(), + cl2deps.end(), + std::inserter(shareddeps, shareddeps.begin()), + node_id_comp); + + /* it's possible some nodes are not shared between this mix node + * inputs, but still needed to be always executed, this mainly + * happens when a node of current subbranch is used by a parent + * node or so */ + if (root_node != node) { + foreach (ShaderInput *in, root_node->inputs) { + ShaderNodeSet rootdeps; + find_dependencies(rootdeps, state->nodes_done, in, node); + set_intersection(rootdeps.begin(), + rootdeps.end(), + cl1deps.begin(), + cl1deps.end(), + std::inserter(shareddeps, shareddeps.begin()), + node_id_comp); + set_intersection(rootdeps.begin(), + rootdeps.end(), + cl2deps.begin(), + cl2deps.end(), + std::inserter(shareddeps, shareddeps.begin()), + node_id_comp); + } + } + + if (!shareddeps.empty()) { + if (cl1in->link) { + generated_shared_closure_nodes(root_node, cl1in->link->parent, state, shareddeps); + } + if (cl2in->link) { + generated_shared_closure_nodes(root_node, cl2in->link->parent, state, shareddeps); + } + + generate_svm_nodes(shareddeps, state); + } + + /* generate instructions for input closure 1 */ + if (cl1in->link) { + /* Add instruction to skip closure and its dependencies if mix + * weight is zero. + */ + current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ONE, 0, stack_assign(facin), 0)); + int node_jump_skip_index = current_svm_nodes.size() - 1; + + generate_multi_closure(root_node, cl1in->link->parent, state); + + /* Fill in jump instruction location to be after closure. */ + current_svm_nodes[node_jump_skip_index].y = current_svm_nodes.size() - + node_jump_skip_index - 1; + } + + /* generate instructions for input closure 2 */ + if (cl2in->link) { + /* Add instruction to skip closure and its dependencies if mix + * weight is zero. + */ + current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ZERO, 0, stack_assign(facin), 0)); + int node_jump_skip_index = current_svm_nodes.size() - 1; + + generate_multi_closure(root_node, cl2in->link->parent, state); + + /* Fill in jump instruction location to be after closure. */ + current_svm_nodes[node_jump_skip_index].y = current_svm_nodes.size() - + node_jump_skip_index - 1; + } + + /* unassign */ + facin->stack_offset = SVM_STACK_INVALID; + } + else { + /* execute closures and their dependencies, no runtime checks + * to skip closures here because was already optimized due to + * fixed weight or add closure that always needs both */ + if (cl1in->link) + generate_multi_closure(root_node, cl1in->link->parent, state); + if (cl2in->link) + generate_multi_closure(root_node, cl2in->link->parent, state); + } + } + else { + generate_closure_node(node, state); + } + + state->nodes_done.insert(node); + state->nodes_done_flag[node->id] = true; } - void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType type) { - /* Converting a shader graph into svm_nodes that can be executed - * sequentially on the virtual machine is fairly simple. We can keep - * looping over nodes and each time all the inputs of a node are - * ready, we add svm_nodes for it that read the inputs from the - * stack and write outputs back to the stack. - * - * With the SVM, we always sample only a single closure. We can think - * of all closures nodes as a binary tree with mix closures as inner - * nodes and other closures as leafs. The SVM will traverse that tree, - * each time deciding to go left or right depending on the mix weights, - * until a closure is found. - * - * We only execute nodes that are needed for the mix weights and chosen - * closure. - */ - - current_type = type; - current_graph = graph; - - /* get input in output node */ - ShaderNode *node = graph->output(); - ShaderInput *clin = NULL; - - switch(type) { - case SHADER_TYPE_SURFACE: - clin = node->input("Surface"); - break; - case SHADER_TYPE_VOLUME: - clin = node->input("Volume"); - break; - case SHADER_TYPE_DISPLACEMENT: - clin = node->input("Displacement"); - break; - case SHADER_TYPE_BUMP: - clin = node->input("Normal"); - break; - default: - assert(0); - break; - } - - /* clear all compiler state */ - memset((void *)&active_stack, 0, sizeof(active_stack)); - current_svm_nodes.clear(); - - foreach(ShaderNode *node_iter, graph->nodes) { - foreach(ShaderInput *input, node_iter->inputs) - input->stack_offset = SVM_STACK_INVALID; - foreach(ShaderOutput *output, node_iter->outputs) - output->stack_offset = SVM_STACK_INVALID; - } - - /* for the bump shader we need add a node to store the shader state */ - bool need_bump_state = (type == SHADER_TYPE_BUMP) && (shader->displacement_method == DISPLACE_BOTH); - int bump_state_offset = SVM_STACK_INVALID; - if(need_bump_state) { - bump_state_offset = stack_find_offset(SVM_BUMP_EVAL_STATE_SIZE); - add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset); - } - - if(shader->used) { - if(clin->link) { - bool generate = false; - - switch(type) { - case SHADER_TYPE_SURFACE: /* generate surface shader */ - generate = true; - shader->has_surface = true; - break; - case SHADER_TYPE_VOLUME: /* generate volume shader */ - generate = true; - shader->has_volume = true; - break; - case SHADER_TYPE_DISPLACEMENT: /* generate displacement shader */ - generate = true; - shader->has_displacement = true; - break; - case SHADER_TYPE_BUMP: /* generate bump shader */ - generate = true; - break; - default: - break; - } - - if(generate) { - CompilerState state(graph); - generate_multi_closure(clin->link->parent, - clin->link->parent, - &state); - } - } - - /* compile output node */ - node->compile(*this); - } - - /* add node to restore state after bump shader has finished */ - if(need_bump_state) { - add_node(NODE_LEAVE_BUMP_EVAL, bump_state_offset); - } - - /* if compile failed, generate empty shader */ - if(compile_failed) { - current_svm_nodes.clear(); - compile_failed = false; - } - - /* for bump shaders we fall thru to the surface shader, but if this is any other kind of shader it ends here */ - if(type != SHADER_TYPE_BUMP) { - add_node(NODE_END, 0, 0, 0); - } -} - -void SVMCompiler::compile(Scene *scene, - Shader *shader, - array<int4>& svm_nodes, - int index, - Summary *summary) -{ - /* copy graph for shader with bump mapping */ - ShaderNode *output = shader->graph->output(); - int start_num_svm_nodes = svm_nodes.size(); - - const double time_start = time_dt(); - - bool has_bump = (shader->displacement_method != DISPLACE_TRUE) && - output->input("Surface")->link && output->input("Displacement")->link; - - /* finalize */ - { - scoped_timer timer((summary != NULL)? &summary->time_finalize: NULL); - shader->graph->finalize(scene, - has_bump, - shader->has_integrator_dependency, - shader->displacement_method == DISPLACE_BOTH); - } - - current_shader = shader; - - shader->has_surface = false; - shader->has_surface_emission = false; - shader->has_surface_transparent = false; - shader->has_surface_bssrdf = false; - shader->has_bump = has_bump; - shader->has_bssrdf_bump = has_bump; - shader->has_volume = false; - shader->has_displacement = false; - shader->has_surface_spatial_varying = false; - shader->has_volume_spatial_varying = false; - shader->has_object_dependency = false; - shader->has_attribute_dependency = false; - shader->has_integrator_dependency = false; - - /* generate bump shader */ - if(has_bump) { - scoped_timer timer((summary != NULL)? &summary->time_generate_bump: NULL); - compile_type(shader, shader->graph, SHADER_TYPE_BUMP); - svm_nodes[index].y = svm_nodes.size(); - svm_nodes.append(current_svm_nodes); - } - - /* generate surface shader */ - { - scoped_timer timer((summary != NULL)? &summary->time_generate_surface: NULL); - compile_type(shader, shader->graph, SHADER_TYPE_SURFACE); - /* only set jump offset if there's no bump shader, as the bump shader will fall thru to this one if it exists */ - if(!has_bump) { - svm_nodes[index].y = svm_nodes.size(); - } - svm_nodes.append(current_svm_nodes); - } - - /* generate volume shader */ - { - scoped_timer timer((summary != NULL)? &summary->time_generate_volume: NULL); - compile_type(shader, shader->graph, SHADER_TYPE_VOLUME); - svm_nodes[index].z = svm_nodes.size(); - svm_nodes.append(current_svm_nodes); - } - - /* generate displacement shader */ - { - scoped_timer timer((summary != NULL)? &summary->time_generate_displacement: NULL); - compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT); - svm_nodes[index].w = svm_nodes.size(); - svm_nodes.append(current_svm_nodes); - } - - /* Fill in summary information. */ - if(summary != NULL) { - summary->time_total = time_dt() - time_start; - summary->peak_stack_usage = max_stack_use; - summary->num_svm_nodes = svm_nodes.size() - start_num_svm_nodes; - } + /* Converting a shader graph into svm_nodes that can be executed + * sequentially on the virtual machine is fairly simple. We can keep + * looping over nodes and each time all the inputs of a node are + * ready, we add svm_nodes for it that read the inputs from the + * stack and write outputs back to the stack. + * + * With the SVM, we always sample only a single closure. We can think + * of all closures nodes as a binary tree with mix closures as inner + * nodes and other closures as leafs. The SVM will traverse that tree, + * each time deciding to go left or right depending on the mix weights, + * until a closure is found. + * + * We only execute nodes that are needed for the mix weights and chosen + * closure. + */ + + current_type = type; + current_graph = graph; + + /* get input in output node */ + ShaderNode *node = graph->output(); + ShaderInput *clin = NULL; + + switch (type) { + case SHADER_TYPE_SURFACE: + clin = node->input("Surface"); + break; + case SHADER_TYPE_VOLUME: + clin = node->input("Volume"); + break; + case SHADER_TYPE_DISPLACEMENT: + clin = node->input("Displacement"); + break; + case SHADER_TYPE_BUMP: + clin = node->input("Normal"); + break; + default: + assert(0); + break; + } + + /* clear all compiler state */ + memset((void *)&active_stack, 0, sizeof(active_stack)); + current_svm_nodes.clear(); + + foreach (ShaderNode *node_iter, graph->nodes) { + foreach (ShaderInput *input, node_iter->inputs) + input->stack_offset = SVM_STACK_INVALID; + foreach (ShaderOutput *output, node_iter->outputs) + output->stack_offset = SVM_STACK_INVALID; + } + + /* for the bump shader we need add a node to store the shader state */ + bool need_bump_state = (type == SHADER_TYPE_BUMP) && + (shader->displacement_method == DISPLACE_BOTH); + int bump_state_offset = SVM_STACK_INVALID; + if (need_bump_state) { + bump_state_offset = stack_find_offset(SVM_BUMP_EVAL_STATE_SIZE); + add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset); + } + + if (shader->used) { + if (clin->link) { + bool generate = false; + + switch (type) { + case SHADER_TYPE_SURFACE: /* generate surface shader */ + generate = true; + shader->has_surface = true; + break; + case SHADER_TYPE_VOLUME: /* generate volume shader */ + generate = true; + shader->has_volume = true; + break; + case SHADER_TYPE_DISPLACEMENT: /* generate displacement shader */ + generate = true; + shader->has_displacement = true; + break; + case SHADER_TYPE_BUMP: /* generate bump shader */ + generate = true; + break; + default: + break; + } + + if (generate) { + CompilerState state(graph); + generate_multi_closure(clin->link->parent, clin->link->parent, &state); + } + } + + /* compile output node */ + node->compile(*this); + } + + /* add node to restore state after bump shader has finished */ + if (need_bump_state) { + add_node(NODE_LEAVE_BUMP_EVAL, bump_state_offset); + } + + /* if compile failed, generate empty shader */ + if (compile_failed) { + current_svm_nodes.clear(); + compile_failed = false; + } + + /* for bump shaders we fall thru to the surface shader, but if this is any other kind of shader it ends here */ + if (type != SHADER_TYPE_BUMP) { + add_node(NODE_END, 0, 0, 0); + } +} + +void SVMCompiler::compile( + Scene *scene, Shader *shader, array<int4> &svm_nodes, int index, Summary *summary) +{ + /* copy graph for shader with bump mapping */ + ShaderNode *output = shader->graph->output(); + int start_num_svm_nodes = svm_nodes.size(); + + const double time_start = time_dt(); + + bool has_bump = (shader->displacement_method != DISPLACE_TRUE) && + output->input("Surface")->link && output->input("Displacement")->link; + + /* finalize */ + { + scoped_timer timer((summary != NULL) ? &summary->time_finalize : NULL); + shader->graph->finalize(scene, + has_bump, + shader->has_integrator_dependency, + shader->displacement_method == DISPLACE_BOTH); + } + + current_shader = shader; + + shader->has_surface = false; + shader->has_surface_emission = false; + shader->has_surface_transparent = false; + shader->has_surface_bssrdf = false; + shader->has_bump = has_bump; + shader->has_bssrdf_bump = has_bump; + shader->has_volume = false; + shader->has_displacement = false; + shader->has_surface_spatial_varying = false; + shader->has_volume_spatial_varying = false; + shader->has_object_dependency = false; + shader->has_attribute_dependency = false; + shader->has_integrator_dependency = false; + + /* generate bump shader */ + if (has_bump) { + scoped_timer timer((summary != NULL) ? &summary->time_generate_bump : NULL); + compile_type(shader, shader->graph, SHADER_TYPE_BUMP); + svm_nodes[index].y = svm_nodes.size(); + svm_nodes.append(current_svm_nodes); + } + + /* generate surface shader */ + { + scoped_timer timer((summary != NULL) ? &summary->time_generate_surface : NULL); + compile_type(shader, shader->graph, SHADER_TYPE_SURFACE); + /* only set jump offset if there's no bump shader, as the bump shader will fall thru to this one if it exists */ + if (!has_bump) { + svm_nodes[index].y = svm_nodes.size(); + } + svm_nodes.append(current_svm_nodes); + } + + /* generate volume shader */ + { + scoped_timer timer((summary != NULL) ? &summary->time_generate_volume : NULL); + compile_type(shader, shader->graph, SHADER_TYPE_VOLUME); + svm_nodes[index].z = svm_nodes.size(); + svm_nodes.append(current_svm_nodes); + } + + /* generate displacement shader */ + { + scoped_timer timer((summary != NULL) ? &summary->time_generate_displacement : NULL); + compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT); + svm_nodes[index].w = svm_nodes.size(); + svm_nodes.append(current_svm_nodes); + } + + /* Fill in summary information. */ + if (summary != NULL) { + summary->time_total = time_dt() - time_start; + summary->peak_stack_usage = max_stack_use; + summary->num_svm_nodes = svm_nodes.size() - start_num_svm_nodes; + } } /* Compiler summary implementation. */ SVMCompiler::Summary::Summary() - : num_svm_nodes(0), - peak_stack_usage(0), - time_finalize(0.0), - time_generate_surface(0.0), - time_generate_bump(0.0), - time_generate_volume(0.0), - time_generate_displacement(0.0), - time_total(0.0) + : num_svm_nodes(0), + peak_stack_usage(0), + time_finalize(0.0), + time_generate_surface(0.0), + time_generate_bump(0.0), + time_generate_volume(0.0), + time_generate_displacement(0.0), + time_total(0.0) { } string SVMCompiler::Summary::full_report() const { - string report = ""; - report += string_printf("Number of SVM nodes: %d\n", num_svm_nodes); - report += string_printf("Peak stack usage: %d\n", peak_stack_usage); + string report = ""; + report += string_printf("Number of SVM nodes: %d\n", num_svm_nodes); + report += string_printf("Peak stack usage: %d\n", peak_stack_usage); - report += string_printf("Time (in seconds):\n"); - report += string_printf("Finalize: %f\n", time_finalize); - report += string_printf(" Surface: %f\n", time_generate_surface); - report += string_printf(" Bump: %f\n", time_generate_bump); - report += string_printf(" Volume: %f\n", time_generate_volume); - report += string_printf(" Displacement: %f\n", time_generate_displacement); - report += string_printf("Generate: %f\n", time_generate_surface + - time_generate_bump + - time_generate_volume + - time_generate_displacement); - report += string_printf("Total: %f\n", time_total); + report += string_printf("Time (in seconds):\n"); + report += string_printf("Finalize: %f\n", time_finalize); + report += string_printf(" Surface: %f\n", time_generate_surface); + report += string_printf(" Bump: %f\n", time_generate_bump); + report += string_printf(" Volume: %f\n", time_generate_volume); + report += string_printf(" Displacement: %f\n", time_generate_displacement); + report += string_printf("Generate: %f\n", + time_generate_surface + time_generate_bump + time_generate_volume + + time_generate_displacement); + report += string_printf("Total: %f\n", time_total); - return report; + return report; } /* Global state of the compiler. */ SVMCompiler::CompilerState::CompilerState(ShaderGraph *graph) { - int max_id = 0; - foreach(ShaderNode *node, graph->nodes) { - max_id = max(node->id, max_id); - } - nodes_done_flag.resize(max_id + 1, false); + int max_id = 0; + foreach (ShaderNode *node, graph->nodes) { + max_id = max(node->id, max_id); + } + nodes_done_flag.resize(max_id + 1, false); } CCL_NAMESPACE_END |