From d1796b8df0b3fecfd08d7611889d0905b8b8e8d7 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Mon, 9 Dec 2024 14:36:35 +0100 Subject: [PATCH] Cycles: Make OSL shader compilation threadsafe and multi-threaded The original OSL Shading System API was stateful: You'd create a shader group, configure it, and then end it. However, this means that only one group can be created at a time. Further, since Cycles reuses the Shading System for multiple instances (e.g. viewport render and material preview), a process-wide mutex is needed. However, for years now OSL has had a better interface, where you explicitly provide the group you refer to. With this, we can not only get rid of the mutex, but actually multi-thread the shader setup even within one instance. Realistically, most time is still spent in the JIT stage, but it's still better than nothing. Pull Request: https://projects.blender.org/blender/blender/pulls/130133 --- intern/cycles/scene/osl.cpp | 137 ++++++++++++++++++++---------------- intern/cycles/scene/osl.h | 3 +- 2 files changed, 77 insertions(+), 63 deletions(-) diff --git a/intern/cycles/scene/osl.cpp b/intern/cycles/scene/osl.cpp index 5038420cea4..726943012e1 100644 --- a/intern/cycles/scene/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -26,6 +26,7 @@ # include "util/path.h" # include "util/progress.h" # include "util/projection.h" +# include "util/task.h" #endif @@ -43,7 +44,6 @@ OSL::ErrorHandler OSLShaderManager::errhandler; map OSLShaderManager::ss_shared; int OSLShaderManager::ss_shared_users = 0; thread_mutex OSLShaderManager::ss_shared_mutex; -thread_mutex OSLShaderManager::ss_mutex; int OSLCompiler::texture_shared_unique_id = 0; @@ -117,35 +117,49 @@ void OSLShaderManager::device_update_specific(Device *device, /* create shaders */ Shader *background_shader = scene->background->get_shader(scene); + /* compile each shader to OSL shader groups */ + TaskPool task_pool; foreach (Shader *shader, scene->shaders) { assert(shader->graph); - if (progress.get_cancel()) - return; + auto compile = [this, scene, shader, background_shader](Device *sub_device) { + OSL::ShadingSystem *ss = ss_shared[sub_device->info.type]; - /* we can only compile one shader at the time as the OSL ShadingSytem - * has a single state, but we put the lock here so different renders can - * compile shaders alternating */ - thread_scoped_lock lock(ss_mutex); + OSLCompiler compiler(this, ss, scene); + compiler.background = (shader == background_shader); + compiler.compile(shader); + }; - device->foreach_device( - [this, scene, shader, background = (shader == background_shader)](Device *sub_device) { - OSLGlobals *og = (OSLGlobals *)sub_device->get_cpu_osl_memory(); - OSL::ShadingSystem *ss = ss_shared[sub_device->info.type]; + task_pool.push(function_bind(&Device::foreach_device, device, compile)); + } + task_pool.wait_work(); - OSLCompiler compiler(this, ss, scene); - compiler.background = background; - compiler.compile(og, shader); - }); + if (progress.get_cancel()) { + return; + } + + /* collect shader groups from all shaders */ + foreach (Shader *shader, scene->shaders) { + device->foreach_device([shader, background_shader](Device *sub_device) { + OSLGlobals *og = (OSLGlobals *)sub_device->get_cpu_osl_memory(); + + /* push state to array for lookup */ + og->surface_state.push_back(shader->osl_surface_ref); + og->volume_state.push_back(shader->osl_volume_ref); + og->displacement_state.push_back(shader->osl_displacement_ref); + og->bump_state.push_back(shader->osl_surface_bump_ref); + + if (shader == background_shader) { + og->background_state = shader->osl_surface_ref; + } + }); if (shader->emission_sampling != EMISSION_SAMPLING_NONE) scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED); } /* setup shader engine */ - int background_id = scene->shader_manager->get_shader_id(background_shader); - - device->foreach_device([background_id](Device *sub_device) { + device->foreach_device([](Device *sub_device) { OSLGlobals *og = (OSLGlobals *)sub_device->get_cpu_osl_memory(); OSL::ShadingSystem *ss = ss_shared[sub_device->info.type]; @@ -153,7 +167,6 @@ void OSLShaderManager::device_update_specific(Device *device, og->ts = ts_shared; og->services = static_cast(ss->renderer()); - og->background_state = og->surface_state[background_id & SHADER_MASK]; og->use = true; }); @@ -848,13 +861,13 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) /* Create shader of the appropriate type. OSL only distinguishes between "surface" * and "displacement" at the moment. */ if (current_type == SHADER_TYPE_SURFACE) - ss->Shader("surface", name, id(node).c_str()); + ss->Shader(*current_group, "surface", name, id(node).c_str()); else if (current_type == SHADER_TYPE_VOLUME) - ss->Shader("surface", name, id(node).c_str()); + ss->Shader(*current_group, "surface", name, id(node).c_str()); else if (current_type == SHADER_TYPE_DISPLACEMENT) - ss->Shader("displacement", name, id(node).c_str()); + ss->Shader(*current_group, "displacement", name, id(node).c_str()); else if (current_type == SHADER_TYPE_BUMP) - ss->Shader("displacement", name, id(node).c_str()); + ss->Shader(*current_group, "displacement", name, id(node).c_str()); else assert(0); @@ -870,7 +883,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) string param_from = compatible_name(input->link->parent, input->link); string param_to = compatible_name(node, input); - ss->ConnectShaders(id_from.c_str(), param_from.c_str(), id_to.c_str(), param_to.c_str()); + ss->ConnectShaders( + *current_group, id_from.c_str(), param_from.c_str(), id_to.c_str(), param_to.c_str()); } } @@ -922,59 +936,62 @@ void OSLCompiler::parameter(ShaderNode *node, const char *name) switch (socket.type) { case SocketType::BOOLEAN: { int value = node->get_bool(socket); - ss->Parameter(name, TypeInt, &value); + ss->Parameter(*current_group, name, TypeInt, &value); break; } case SocketType::FLOAT: { float value = node->get_float(socket); - ss->Parameter(uname, TypeFloat, &value); + ss->Parameter(*current_group, uname, TypeFloat, &value); break; } case SocketType::INT: { int value = node->get_int(socket); - ss->Parameter(uname, TypeInt, &value); + ss->Parameter(*current_group, uname, TypeInt, &value); break; } case SocketType::COLOR: { float3 value = node->get_float3(socket); - ss->Parameter(uname, TypeColor, &value); + ss->Parameter(*current_group, uname, TypeColor, &value); break; } case SocketType::VECTOR: { float3 value = node->get_float3(socket); - ss->Parameter(uname, TypeVector, &value); + ss->Parameter(*current_group, uname, TypeVector, &value); break; } case SocketType::POINT: { float3 value = node->get_float3(socket); - ss->Parameter(uname, TypePoint, &value); + ss->Parameter(*current_group, uname, TypePoint, &value); break; } case SocketType::NORMAL: { float3 value = node->get_float3(socket); - ss->Parameter(uname, TypeNormal, &value); + ss->Parameter(*current_group, uname, TypeNormal, &value); break; } case SocketType::POINT2: { float2 value = node->get_float2(socket); - ss->Parameter(uname, TypeDesc(TypeDesc::FLOAT, TypeDesc::VEC2, TypeDesc::POINT), &value); + ss->Parameter(*current_group, + uname, + TypeDesc(TypeDesc::FLOAT, TypeDesc::VEC2, TypeDesc::POINT), + &value); break; } case SocketType::STRING: { ustring value = node->get_string(socket); - ss->Parameter(uname, TypeString, &value); + ss->Parameter(*current_group, uname, TypeString, &value); break; } case SocketType::ENUM: { ustring value = node->get_string(socket); - ss->Parameter(uname, TypeString, &value); + ss->Parameter(*current_group, uname, TypeString, &value); break; } case SocketType::TRANSFORM: { Transform value = node->get_transform(socket); ProjectionTransform projection(value); projection = projection_transpose(projection); - ss->Parameter(uname, TypeMatrix, &projection); + ss->Parameter(*current_group, uname, TypeMatrix, &projection); break; } case SocketType::BOOLEAN_ARRAY: { @@ -983,17 +1000,17 @@ void OSLCompiler::parameter(ShaderNode *node, const char *name) array intvalue(value.size()); for (size_t i = 0; i < value.size(); i++) intvalue[i] = value[i]; - ss->Parameter(uname, array_typedesc(TypeInt, value.size()), intvalue.data()); + ss->Parameter(*current_group, uname, array_typedesc(TypeInt, value.size()), intvalue.data()); break; } case SocketType::FLOAT_ARRAY: { const array &value = node->get_float_array(socket); - ss->Parameter(uname, array_typedesc(TypeFloat, value.size()), value.data()); + ss->Parameter(*current_group, uname, array_typedesc(TypeFloat, value.size()), value.data()); break; } case SocketType::INT_ARRAY: { const array &value = node->get_int_array(socket); - ss->Parameter(uname, array_typedesc(TypeInt, value.size()), value.data()); + ss->Parameter(*current_group, uname, array_typedesc(TypeInt, value.size()), value.data()); break; } case SocketType::COLOR_ARRAY: @@ -1029,12 +1046,13 @@ void OSLCompiler::parameter(ShaderNode *node, const char *name) fvalue[j++] = value[i].z; } - ss->Parameter(uname, array_typedesc(typedesc, value.size()), fvalue.data()); + ss->Parameter(*current_group, uname, array_typedesc(typedesc, value.size()), fvalue.data()); break; } case SocketType::POINT2_ARRAY: { const array &value = node->get_float2_array(socket); ss->Parameter( + *current_group, uname, array_typedesc(TypeDesc(TypeDesc::FLOAT, TypeDesc::VEC2, TypeDesc::POINT), value.size()), value.data()); @@ -1042,7 +1060,7 @@ void OSLCompiler::parameter(ShaderNode *node, const char *name) } case SocketType::STRING_ARRAY: { const array &value = node->get_string_array(socket); - ss->Parameter(uname, array_typedesc(TypeString, value.size()), value.data()); + ss->Parameter(*current_group, uname, array_typedesc(TypeString, value.size()), value.data()); break; } case SocketType::TRANSFORM_ARRAY: { @@ -1051,7 +1069,8 @@ void OSLCompiler::parameter(ShaderNode *node, const char *name) for (size_t i = 0; i < value.size(); i++) { fvalue[i] = projection_transpose(ProjectionTransform(value[i])); } - ss->Parameter(uname, array_typedesc(TypeMatrix, fvalue.size()), fvalue.data()); + ss->Parameter( + *current_group, uname, array_typedesc(TypeMatrix, fvalue.size()), fvalue.data()); break; } case SocketType::CLOSURE: @@ -1069,57 +1088,57 @@ void OSLCompiler::parameter(ShaderNode *node, const char *name) void OSLCompiler::parameter(const char *name, float f) { - ss->Parameter(name, TypeFloat, &f); + ss->Parameter(*current_group, name, TypeFloat, &f); } void OSLCompiler::parameter_color(const char *name, float3 f) { - ss->Parameter(name, TypeColor, &f); + ss->Parameter(*current_group, name, TypeColor, &f); } void OSLCompiler::parameter_point(const char *name, float3 f) { - ss->Parameter(name, TypePoint, &f); + ss->Parameter(*current_group, name, TypePoint, &f); } void OSLCompiler::parameter_normal(const char *name, float3 f) { - ss->Parameter(name, TypeNormal, &f); + ss->Parameter(*current_group, name, TypeNormal, &f); } void OSLCompiler::parameter_vector(const char *name, float3 f) { - ss->Parameter(name, TypeVector, &f); + ss->Parameter(*current_group, name, TypeVector, &f); } void OSLCompiler::parameter(const char *name, int f) { - ss->Parameter(name, TypeInt, &f); + ss->Parameter(*current_group, name, TypeInt, &f); } void OSLCompiler::parameter(const char *name, const char *s) { - ss->Parameter(name, TypeString, &s); + ss->Parameter(*current_group, name, TypeString, &s); } void OSLCompiler::parameter(const char *name, ustring s) { const char *str = s.c_str(); - ss->Parameter(name, TypeString, &str); + ss->Parameter(*current_group, name, TypeString, &str); } void OSLCompiler::parameter(const char *name, const Transform &tfm) { ProjectionTransform projection(tfm); projection = projection_transpose(projection); - ss->Parameter(name, TypeMatrix, (float *)&projection); + ss->Parameter(*current_group, name, TypeMatrix, (float *)&projection); } void OSLCompiler::parameter_array(const char *name, const float f[], int arraylen) { TypeDesc type = TypeFloat; type.arraylen = arraylen; - ss->Parameter(name, type, f); + ss->Parameter(*current_group, name, type, f); } void OSLCompiler::parameter_color_array(const char *name, const array &f) @@ -1135,7 +1154,7 @@ void OSLCompiler::parameter_color_array(const char *name, const array &f TypeDesc type = TypeColor; type.arraylen = table.size(); - ss->Parameter(name, type, table.data()); + ss->Parameter(*current_group, name, type, table.data()); } void OSLCompiler::parameter_attribute(const char *name, ustring s) @@ -1217,7 +1236,7 @@ OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph name.imbue(std::locale("C")); name << "shader_" << shader->name.hash(); - OSL::ShaderGroupRef group = ss->ShaderGroupBegin(name.str()); + current_group = ss->ShaderGroupBegin(name.str()); ShaderNode *output = graph->output(); ShaderNodeSet dependencies; @@ -1249,12 +1268,12 @@ OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph else assert(0); - ss->ShaderGroupEnd(); + ss->ShaderGroupEnd(*current_group); - return group; + return std::move(current_group); } -void OSLCompiler::compile(OSLGlobals *og, Shader *shader) +void OSLCompiler::compile(Shader *shader) { if (shader->is_modified()) { ShaderGraph *graph = shader->graph; @@ -1315,12 +1334,6 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader) /* Estimate emission for MIS. */ shader->estimate_emission(); } - - /* push state to array for lookup */ - og->surface_state.push_back(shader->osl_surface_ref); - og->volume_state.push_back(shader->osl_volume_ref); - og->displacement_state.push_back(shader->osl_displacement_ref); - og->bump_state.push_back(shader->osl_surface_bump_ref); } void OSLCompiler::parameter_texture(const char *name, ustring filename, ustring colorspace) diff --git a/intern/cycles/scene/osl.h b/intern/cycles/scene/osl.h index 3cb2c23556e..d3b94d896dd 100644 --- a/intern/cycles/scene/osl.h +++ b/intern/cycles/scene/osl.h @@ -126,7 +126,7 @@ class OSLCompiler { #ifdef WITH_OSL OSLCompiler(OSLShaderManager *manager, OSL::ShadingSystem *shadingsys, Scene *scene); #endif - void compile(OSLGlobals *og, Shader *shader); + void compile(Shader *shader); void add(ShaderNode *node, const char *name, bool isfilepath = false); @@ -177,6 +177,7 @@ class OSLCompiler { ShaderType current_type; Shader *current_shader; + OSL::ShaderGroupRef current_group; static int texture_shared_unique_id; };