Changeset View
Changeset View
Standalone View
Standalone View
intern/cycles/scene/osl.cpp
| Show All 32 Lines | |||||
| #ifdef WITH_OSL | #ifdef WITH_OSL | ||||
| /* Shared Texture and Shading System */ | /* Shared Texture and Shading System */ | ||||
| OSL::TextureSystem *OSLShaderManager::ts_shared = NULL; | OSL::TextureSystem *OSLShaderManager::ts_shared = NULL; | ||||
| int OSLShaderManager::ts_shared_users = 0; | int OSLShaderManager::ts_shared_users = 0; | ||||
| thread_mutex OSLShaderManager::ts_shared_mutex; | thread_mutex OSLShaderManager::ts_shared_mutex; | ||||
| OSL::ShadingSystem *OSLShaderManager::ss_shared = NULL; | OSL::ErrorHandler OSLShaderManager::errhandler; | ||||
| OSLRenderServices *OSLShaderManager::services_shared = NULL; | map<int, OSL::ShadingSystem *> OSLShaderManager::ss_shared; | ||||
| int OSLShaderManager::ss_shared_users = 0; | int OSLShaderManager::ss_shared_users = 0; | ||||
| thread_mutex OSLShaderManager::ss_shared_mutex; | thread_mutex OSLShaderManager::ss_shared_mutex; | ||||
| thread_mutex OSLShaderManager::ss_mutex; | thread_mutex OSLShaderManager::ss_mutex; | ||||
| int OSLCompiler::texture_shared_unique_id = 0; | int OSLCompiler::texture_shared_unique_id = 0; | ||||
| /* Shader Manager */ | /* Shader Manager */ | ||||
| OSLShaderManager::OSLShaderManager() | OSLShaderManager::OSLShaderManager(Device *device) : device_(device) | ||||
| { | { | ||||
| texture_system_init(); | texture_system_init(); | ||||
| shading_system_init(); | shading_system_init(); | ||||
| } | } | ||||
| OSLShaderManager::~OSLShaderManager() | OSLShaderManager::~OSLShaderManager() | ||||
| { | { | ||||
| shading_system_free(); | shading_system_free(); | ||||
| ▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | if (scene->update_stats) { | ||||
| scene->update_stats->osl.times.add_entry({"device_update", time}); | scene->update_stats->osl.times.add_entry({"device_update", time}); | ||||
| } | } | ||||
| }); | }); | ||||
| VLOG_INFO << "Total " << scene->shaders.size() << " shaders."; | VLOG_INFO << "Total " << scene->shaders.size() << " shaders."; | ||||
| device_free(device, dscene, scene); | device_free(device, dscene, scene); | ||||
| /* set texture system */ | /* set texture system (only on CPU devices, since GPU devices cannot use OIIO) */ | ||||
| scene->image_manager->set_osl_texture_system((void *)ts); | if (device->info.type == DEVICE_CPU) { | ||||
| scene->image_manager->set_osl_texture_system((void *)ts_shared); | |||||
| } | |||||
| /* create shaders */ | /* create shaders */ | ||||
| OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); | |||||
| Shader *background_shader = scene->background->get_shader(scene); | Shader *background_shader = scene->background->get_shader(scene); | ||||
| foreach (Shader *shader, scene->shaders) { | foreach (Shader *shader, scene->shaders) { | ||||
| assert(shader->graph); | assert(shader->graph); | ||||
| if (progress.get_cancel()) | if (progress.get_cancel()) | ||||
| return; | return; | ||||
| /* we can only compile one shader at the time as the OSL ShadingSytem | /* 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 | * has a single state, but we put the lock here so different renders can | ||||
| * compile shaders alternating */ | * compile shaders alternating */ | ||||
| thread_scoped_lock lock(ss_mutex); | thread_scoped_lock lock(ss_mutex); | ||||
| OSLCompiler compiler(this, services, ss, scene); | device->foreach_device( | ||||
| compiler.background = (shader == background_shader); | [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]; | |||||
| OSLCompiler compiler(this, ss, scene); | |||||
| compiler.background = background; | |||||
| compiler.compile(og, shader); | compiler.compile(og, shader); | ||||
| }); | |||||
| if (shader->get_use_mis() && shader->has_surface_emission) | if (shader->get_use_mis() && shader->has_surface_emission) | ||||
| scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED); | scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED); | ||||
| } | } | ||||
| /* setup shader engine */ | /* setup shader engine */ | ||||
| int background_id = scene->shader_manager->get_shader_id(background_shader); | |||||
| device->foreach_device([background_id](Device *sub_device) { | |||||
| OSLGlobals *og = (OSLGlobals *)sub_device->get_cpu_osl_memory(); | |||||
| OSL::ShadingSystem *ss = ss_shared[sub_device->info.type]; | |||||
| og->ss = ss; | og->ss = ss; | ||||
| og->ts = ts; | og->ts = ts_shared; | ||||
| og->services = services; | og->services = static_cast<OSLRenderServices *>(ss->renderer()); | ||||
| int background_id = scene->shader_manager->get_shader_id(background_shader); | |||||
| og->background_state = og->surface_state[background_id & SHADER_MASK]; | og->background_state = og->surface_state[background_id & SHADER_MASK]; | ||||
| og->use = true; | og->use = true; | ||||
| }); | |||||
| foreach (Shader *shader, scene->shaders) | foreach (Shader *shader, scene->shaders) | ||||
| shader->clear_modified(); | shader->clear_modified(); | ||||
| update_flags = UPDATE_NONE; | update_flags = UPDATE_NONE; | ||||
| /* add special builtin texture types */ | /* add special builtin texture types */ | ||||
| for (const auto &[device_type, ss] : ss_shared) { | |||||
| OSLRenderServices *services = static_cast<OSLRenderServices *>(ss->renderer()); | |||||
| services->textures.insert(ustring("@ao"), new OSLTextureHandle(OSLTextureHandle::AO)); | services->textures.insert(ustring("@ao"), new OSLTextureHandle(OSLTextureHandle::AO)); | ||||
| services->textures.insert(ustring("@bevel"), new OSLTextureHandle(OSLTextureHandle::BEVEL)); | services->textures.insert(ustring("@bevel"), new OSLTextureHandle(OSLTextureHandle::BEVEL)); | ||||
| } | |||||
| device_update_common(device, dscene, scene, progress); | device_update_common(device, dscene, scene, progress); | ||||
| { | { | ||||
| /* Perform greedyjit optimization. | /* Perform greedyjit optimization. | ||||
| * | * | ||||
| * This might waste time on optimizing groups which are never actually | * This might waste time on optimizing groups which are never actually | ||||
| * used, but this prevents OSL from allocating data on TLS at render | * used, but this prevents OSL from allocating data on TLS at render | ||||
| * time. | * time. | ||||
| * | * | ||||
| * This is much better for us because this way we aren't required to | * This is much better for us because this way we aren't required to | ||||
| * stop task scheduler threads to make sure all TLS is clean and don't | * stop task scheduler threads to make sure all TLS is clean and don't | ||||
| * have issues with TLS data free accessing freed memory if task scheduler | * have issues with TLS data free accessing freed memory if task scheduler | ||||
| * is being freed after the Session is freed. | * is being freed after the Session is freed. | ||||
| */ | */ | ||||
| thread_scoped_lock lock(ss_shared_mutex); | thread_scoped_lock lock(ss_shared_mutex); | ||||
| for (const auto &[device_type, ss] : ss_shared) { | |||||
| ss->optimize_all_groups(); | ss->optimize_all_groups(); | ||||
| } | } | ||||
| } | } | ||||
| /* load kernels */ | |||||
| if (!device->load_osl_kernels()) { | |||||
| progress.set_error(device->error_message()); | |||||
| } | |||||
| } | |||||
| void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene) | void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene) | ||||
| { | { | ||||
| OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); | |||||
| device_free_common(device, dscene, scene); | device_free_common(device, dscene, scene); | ||||
| /* clear shader engine */ | /* clear shader engine */ | ||||
brecht: The OptiX specific part of this should move into the device, though not exactly sure what the… | |||||
| device->foreach_device([](Device *sub_device) { | |||||
| OSLGlobals *og = (OSLGlobals *)sub_device->get_cpu_osl_memory(); | |||||
| og->use = false; | og->use = false; | ||||
| og->ss = NULL; | og->ss = NULL; | ||||
| og->ts = NULL; | og->ts = NULL; | ||||
| og->surface_state.clear(); | og->surface_state.clear(); | ||||
| og->volume_state.clear(); | og->volume_state.clear(); | ||||
| og->displacement_state.clear(); | og->displacement_state.clear(); | ||||
| og->bump_state.clear(); | og->bump_state.clear(); | ||||
| og->background_state.reset(); | og->background_state.reset(); | ||||
| }); | |||||
| } | } | ||||
| void OSLShaderManager::texture_system_init() | void OSLShaderManager::texture_system_init() | ||||
| { | { | ||||
| /* create texture system, shared between different renders to reduce memory usage */ | /* create texture system, shared between different renders to reduce memory usage */ | ||||
| thread_scoped_lock lock(ts_shared_mutex); | thread_scoped_lock lock(ts_shared_mutex); | ||||
| if (ts_shared_users == 0) { | if (ts_shared_users++ == 0) { | ||||
| ts_shared = TextureSystem::create(true); | ts_shared = TextureSystem::create(true); | ||||
| ts_shared->attribute("automip", 1); | ts_shared->attribute("automip", 1); | ||||
| ts_shared->attribute("autotile", 64); | ts_shared->attribute("autotile", 64); | ||||
| ts_shared->attribute("gray_to_rgb", 1); | ts_shared->attribute("gray_to_rgb", 1); | ||||
| /* effectively unlimited for now, until we support proper mipmap lookups */ | /* effectively unlimited for now, until we support proper mipmap lookups */ | ||||
| ts_shared->attribute("max_memory_MB", 16384); | ts_shared->attribute("max_memory_MB", 16384); | ||||
| } | } | ||||
| ts = ts_shared; | |||||
| ts_shared_users++; | |||||
| } | } | ||||
| void OSLShaderManager::texture_system_free() | void OSLShaderManager::texture_system_free() | ||||
| { | { | ||||
| /* shared texture system decrease users and destroy if no longer used */ | /* shared texture system decrease users and destroy if no longer used */ | ||||
| thread_scoped_lock lock(ts_shared_mutex); | thread_scoped_lock lock(ts_shared_mutex); | ||||
| ts_shared_users--; | |||||
| if (ts_shared_users == 0) { | if (--ts_shared_users == 0) { | ||||
| ts_shared->invalidate_all(true); | ts_shared->invalidate_all(true); | ||||
| OSL::TextureSystem::destroy(ts_shared); | OSL::TextureSystem::destroy(ts_shared); | ||||
| ts_shared = NULL; | ts_shared = NULL; | ||||
| } | } | ||||
| ts = NULL; | |||||
| } | } | ||||
| void OSLShaderManager::shading_system_init() | void OSLShaderManager::shading_system_init() | ||||
| { | { | ||||
| /* create shading system, shared between different renders to reduce memory usage */ | /* create shading system, shared between different renders to reduce memory usage */ | ||||
| thread_scoped_lock lock(ss_shared_mutex); | thread_scoped_lock lock(ss_shared_mutex); | ||||
| if (ss_shared_users == 0) { | device_->foreach_device([](Device *sub_device) { | ||||
| const DeviceType device_type = sub_device->info.type; | |||||
| if (ss_shared_users++ == 0 || ss_shared.find(device_type) == ss_shared.end()) { | |||||
| /* Must use aligned new due to concurrent hash map. */ | /* Must use aligned new due to concurrent hash map. */ | ||||
| services_shared = util_aligned_new<OSLRenderServices>(ts_shared); | OSLRenderServices *services = util_aligned_new<OSLRenderServices>(ts_shared, device_type); | ||||
| string shader_path = path_get("shader"); | string shader_path = path_get("shader"); | ||||
| # ifdef _WIN32 | # ifdef _WIN32 | ||||
| /* Annoying thing, Cycles stores paths in UTF-8 codepage, so it can | /* Annoying thing, Cycles stores paths in UTF-8 codepage, so it can | ||||
| * operate with file paths with any character. This requires to use wide | * operate with file paths with any character. This requires to use wide | ||||
| * char functions, but OSL uses old fashioned ANSI functions which means: | * char functions, but OSL uses old fashioned ANSI functions which means: | ||||
| * | * | ||||
| * - We have to convert our paths to ANSI before passing to OSL | * - We have to convert our paths to ANSI before passing to OSL | ||||
| * - OSL can't be used when there's a multi-byte character in the path | * - OSL can't be used when there's a multi-byte character in the path | ||||
| * to the shaders folder. | * to the shaders folder. | ||||
| */ | */ | ||||
| shader_path = string_to_ansi(shader_path); | shader_path = string_to_ansi(shader_path); | ||||
| # endif | # endif | ||||
| ss_shared = new OSL::ShadingSystem(services_shared, ts_shared, &errhandler); | OSL::ShadingSystem *ss = new OSL::ShadingSystem(services, ts_shared, &errhandler); | ||||
| ss_shared->attribute("lockgeom", 1); | ss->attribute("lockgeom", 1); | ||||
| ss_shared->attribute("commonspace", "world"); | ss->attribute("commonspace", "world"); | ||||
| ss_shared->attribute("searchpath:shader", shader_path); | ss->attribute("searchpath:shader", shader_path); | ||||
| ss_shared->attribute("greedyjit", 1); | ss->attribute("greedyjit", 1); | ||||
| VLOG_INFO << "Using shader search path: " << shader_path; | VLOG_INFO << "Using shader search path: " << shader_path; | ||||
| /* our own ray types */ | /* our own ray types */ | ||||
| static const char *raytypes[] = { | static const char *raytypes[] = { | ||||
| "camera", /* PATH_RAY_CAMERA */ | "camera", /* PATH_RAY_CAMERA */ | ||||
| "reflection", /* PATH_RAY_REFLECT */ | "reflection", /* PATH_RAY_REFLECT */ | ||||
| "refraction", /* PATH_RAY_TRANSMIT */ | "refraction", /* PATH_RAY_TRANSMIT */ | ||||
| "diffuse", /* PATH_RAY_DIFFUSE */ | "diffuse", /* PATH_RAY_DIFFUSE */ | ||||
| "glossy", /* PATH_RAY_GLOSSY */ | "glossy", /* PATH_RAY_GLOSSY */ | ||||
| "singular", /* PATH_RAY_SINGULAR */ | "singular", /* PATH_RAY_SINGULAR */ | ||||
| "transparent", /* PATH_RAY_TRANSPARENT */ | "transparent", /* PATH_RAY_TRANSPARENT */ | ||||
| "volume_scatter", /* PATH_RAY_VOLUME_SCATTER */ | "volume_scatter", /* PATH_RAY_VOLUME_SCATTER */ | ||||
| "shadow", /* PATH_RAY_SHADOW_OPAQUE */ | "shadow", /* PATH_RAY_SHADOW_OPAQUE */ | ||||
| "shadow", /* PATH_RAY_SHADOW_TRANSPARENT */ | "shadow", /* PATH_RAY_SHADOW_TRANSPARENT */ | ||||
| "__unused__", /* PATH_RAY_NODE_UNALIGNED */ | "__unused__", /* PATH_RAY_NODE_UNALIGNED */ | ||||
| "__unused__", /* PATH_RAY_MIS_SKIP */ | "__unused__", /* PATH_RAY_MIS_SKIP */ | ||||
| "diffuse_ancestor", /* PATH_RAY_DIFFUSE_ANCESTOR */ | "diffuse_ancestor", /* PATH_RAY_DIFFUSE_ANCESTOR */ | ||||
| /* Remaining irrelevant bits up to 32. */ | /* Remaining irrelevant bits up to 32. */ | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| "__unused__", | "__unused__", | ||||
| }; | }; | ||||
| const int nraytypes = sizeof(raytypes) / sizeof(raytypes[0]); | const int nraytypes = sizeof(raytypes) / sizeof(raytypes[0]); | ||||
| ss_shared->attribute("raytypes", TypeDesc(TypeDesc::STRING, nraytypes), raytypes); | ss->attribute("raytypes", TypeDesc(TypeDesc::STRING, nraytypes), raytypes); | ||||
| OSLRenderServices::register_closures(ss_shared); | OSLRenderServices::register_closures(ss); | ||||
| loaded_shaders.clear(); | ss_shared[device_type] = ss; | ||||
| } | } | ||||
| }); | |||||
| ss = ss_shared; | loaded_shaders.clear(); | ||||
| services = services_shared; | |||||
| ss_shared_users++; | |||||
| } | } | ||||
| void OSLShaderManager::shading_system_free() | void OSLShaderManager::shading_system_free() | ||||
| { | { | ||||
| /* shared shading system decrease users and destroy if no longer used */ | /* shared shading system decrease users and destroy if no longer used */ | ||||
| thread_scoped_lock lock(ss_shared_mutex); | thread_scoped_lock lock(ss_shared_mutex); | ||||
| ss_shared_users--; | |||||
| if (ss_shared_users == 0) { | device_->foreach_device([](Device * /*sub_device*/) { | ||||
| delete ss_shared; | if (--ss_shared_users == 0) { | ||||
| ss_shared = NULL; | for (const auto &[device_type, ss] : ss_shared) { | ||||
| OSLRenderServices *services = static_cast<OSLRenderServices *>(ss->renderer()); | |||||
| util_aligned_delete(services_shared); | delete ss; | ||||
| services_shared = NULL; | |||||
| util_aligned_delete(services); | |||||
| } | } | ||||
| ss = NULL; | ss_shared.clear(); | ||||
| services = NULL; | } | ||||
| }); | |||||
| } | } | ||||
| bool OSLShaderManager::osl_compile(const string &inputfile, const string &outputfile) | bool OSLShaderManager::osl_compile(const string &inputfile, const string &outputfile) | ||||
| { | { | ||||
| vector<string> options; | vector<string> options; | ||||
| string stdosl_path; | string stdosl_path; | ||||
| string shader_path = path_get("shader"); | string shader_path = path_get("shader"); | ||||
| ▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | if (!path_read_text(filepath, bytecode)) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| return shader_load_bytecode(bytecode_hash, bytecode); | return shader_load_bytecode(bytecode_hash, bytecode); | ||||
| } | } | ||||
| const char *OSLShaderManager::shader_load_bytecode(const string &hash, const string &bytecode) | const char *OSLShaderManager::shader_load_bytecode(const string &hash, const string &bytecode) | ||||
| { | { | ||||
| for (const auto &[device_type, ss] : ss_shared) { | |||||
| ss->LoadMemoryCompiledShader(hash.c_str(), bytecode.c_str()); | ss->LoadMemoryCompiledShader(hash.c_str(), bytecode.c_str()); | ||||
| } | |||||
| OSLShaderInfo info; | OSLShaderInfo info; | ||||
| if (!info.query.open_bytecode(bytecode)) { | if (!info.query.open_bytecode(bytecode)) { | ||||
| fprintf(stderr, "OSL query error: %s\n", info.query.geterror().c_str()); | fprintf(stderr, "OSL query error: %s\n", info.query.geterror().c_str()); | ||||
| } | } | ||||
| /* this is a bit weak, but works */ | /* this is a bit weak, but works */ | ||||
| ▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | OSLNode *OSLShaderManager::osl_node(ShaderGraph *graph, | ||||
| /* Generate inputs and outputs */ | /* Generate inputs and outputs */ | ||||
| node->create_inputs_outputs(node->type); | node->create_inputs_outputs(node->type); | ||||
| return node; | return node; | ||||
| } | } | ||||
| /* Graph Compiler */ | /* Graph Compiler */ | ||||
| OSLCompiler::OSLCompiler(OSLShaderManager *manager, | OSLCompiler::OSLCompiler(OSLShaderManager *manager, OSL::ShadingSystem *ss, Scene *scene) | ||||
| OSLRenderServices *services, | : scene(scene), | ||||
| OSL::ShadingSystem *ss, | manager(manager), | ||||
| Scene *scene) | services(static_cast<OSLRenderServices *>(ss->renderer())), | ||||
| : scene(scene), manager(manager), services(services), ss(ss) | ss(ss) | ||||
| { | { | ||||
| current_type = SHADER_TYPE_SURFACE; | current_type = SHADER_TYPE_SURFACE; | ||||
| current_shader = NULL; | current_shader = NULL; | ||||
| background = false; | background = false; | ||||
| } | } | ||||
| string OSLCompiler::id(ShaderNode *node) | string OSLCompiler::id(ShaderNode *node) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 485 Lines • ▼ Show 20 Lines | do { | ||||
| } | } | ||||
| } while (!nodes_done); | } while (!nodes_done); | ||||
| } | } | ||||
| OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType type) | OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType type) | ||||
| { | { | ||||
| current_type = type; | current_type = type; | ||||
| OSL::ShaderGroupRef group = ss->ShaderGroupBegin(shader->name.c_str()); | string name = shader->name.string(); | ||||
| /* Replace invalid characters. */ | |||||
| for (size_t i; (i = name.find_first_of(" .,:;+-*/#")) != string::npos;) | |||||
| name.replace(i, 1, "_"); | |||||
| OSL::ShaderGroupRef group = ss->ShaderGroupBegin(name); | |||||
| ShaderNode *output = graph->output(); | ShaderNode *output = graph->output(); | ||||
| ShaderNodeSet dependencies; | ShaderNodeSet dependencies; | ||||
| if (type == SHADER_TYPE_SURFACE) { | if (type == SHADER_TYPE_SURFACE) { | ||||
| /* generate surface shader */ | /* generate surface shader */ | ||||
| find_dependencies(dependencies, output->input("Surface")); | find_dependencies(dependencies, output->input("Surface")); | ||||
| generate_nodes(dependencies); | generate_nodes(dependencies); | ||||
| ▲ Show 20 Lines • Show All 199 Lines • Show Last 20 Lines | |||||
The OptiX specific part of this should move into the device, though not exactly sure what the device API for that should look like.