diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp index 837a8186064..32911e054fe 100644 --- a/intern/cycles/device/device_cpu.cpp +++ b/intern/cycles/device/device_cpu.cpp @@ -43,6 +43,7 @@ #include "render/buffers.h" #include "render/coverage.h" +#include "util/util_aligned_malloc.h" #include "util/util_debug.h" #include "util/util_foreach.h" #include "util/util_function.h" @@ -165,7 +166,7 @@ class CPUDevice : public Device { bool need_texture_info; #ifdef WITH_OSL - OSLGlobals osl_globals; + OSLGlobals *osl_globals; #endif bool use_split_kernel; @@ -282,7 +283,9 @@ class CPUDevice : public Device { } #ifdef WITH_OSL - kernel_globals.osl = &osl_globals; + /* Must use aligned malloc due to concurrent hash map. */ + osl_globals = util_aligned_new(); + kernel_globals.osl = osl_globals; #endif use_split_kernel = DebugFlags().cpu.split_kernel; if (use_split_kernel) { @@ -317,6 +320,9 @@ class CPUDevice : public Device { ~CPUDevice() { +#ifdef WITH_OSL + delete osl_globals; +#endif task_pool.stop(); texture_info.free(); } @@ -492,7 +498,7 @@ class CPUDevice : public Device { void *osl_memory() { #ifdef WITH_OSL - return &osl_globals; + return osl_globals; #else return NULL; #endif @@ -981,7 +987,7 @@ class CPUDevice : public Device { KernelGlobals kg = kernel_globals; #ifdef WITH_OSL - OSLShader::thread_init(&kg, &kernel_globals, &osl_globals); + OSLShader::thread_init(&kg, &kernel_globals, osl_globals); #endif for (int sample = 0; sample < task.num_samples; sample++) { for (int x = task.shader_x; x < task.shader_x + task.shader_w; x++) @@ -1053,7 +1059,7 @@ class CPUDevice : public Device { kg.decoupled_volume_steps_index = 0; kg.coverage_asset = kg.coverage_object = kg.coverage_material = NULL; #ifdef WITH_OSL - OSLShader::thread_init(&kg, &kernel_globals, &osl_globals); + OSLShader::thread_init(&kg, &kernel_globals, osl_globals); #endif return kg; } diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h index 641c9967586..414aaf891db 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/osl_globals.h @@ -21,10 +21,14 @@ # include +# include +# include + # include "util/util_map.h" # include "util/util_param.h" # include "util/util_thread.h" # include "util/util_vector.h" +# include "util/util_unique_ptr.h" # ifndef WIN32 using std::isfinite; @@ -34,6 +38,48 @@ CCL_NAMESPACE_BEGIN class OSLRenderServices; +/* OSL Texture Handle + * + * OSL texture lookups are string based. If those strings are known at compile + * time, the OSL compiler can cache a texture handle to use instead of a string. + * + * By default it uses TextureSystem::TextureHandle. But since we want to support + * different kinds of textures and color space conversions, this is our own handle + * with additional data. + * + * These are stored in a concurrent hash map, because OSL can compile multiple + * shaders in parallel. */ + +struct OSLTextureHandle : public OIIO::RefCnt { + enum Type { OIIO, SVM, IES, BEVEL, AO }; + + OSLTextureHandle() : type(OIIO), svm_slot(-1), oiio_handle(NULL) + { + } + + OSLTextureHandle(Type type) : type(type), svm_slot(-1), oiio_handle(NULL) + { + } + + OSLTextureHandle(Type type, int svm_slot) : type(type), svm_slot(svm_slot), oiio_handle(NULL) + { + } + + Type type; + int svm_slot; + OSL::TextureSystem::TextureHandle *oiio_handle; +}; + +typedef OIIO::intrusive_ptr OSLTextureHandleRef; +typedef OIIO::unordered_map_concurrent + OSLTextureHandleMap; + +/* OSL Globals + * + * Data needed by OSL render services, that is global to a rendering session. + * This includes all OSL shaders, name to attribute mapping and texture handles. + * */ + struct OSLGlobals { OSLGlobals() { @@ -70,6 +116,9 @@ struct OSLGlobals { vector attribute_map; ObjectNameMap object_name_map; vector object_names; + + /* textures */ + OSLTextureHandleMap textures; }; /* trace() call result */ diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index b4329ab0a71..7de596a2c30 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -124,8 +124,6 @@ ustring OSLRenderServices::u_I("I"); ustring OSLRenderServices::u_u("u"); ustring OSLRenderServices::u_v("v"); ustring OSLRenderServices::u_empty; -ustring OSLRenderServices::u_at_bevel("@bevel"); -ustring OSLRenderServices::u_at_ao("@ao"); OSLRenderServices::OSLRenderServices() { @@ -154,7 +152,7 @@ void OSLRenderServices::thread_init(KernelGlobals *kernel_globals_, OSL::TextureSystem *osl_ts_) { kernel_globals = kernel_globals_; - osl_globals = osl_globals; + osl_globals = osl_globals_; osl_ts = osl_ts_; } @@ -956,19 +954,44 @@ bool OSLRenderServices::get_userdata( TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(ustring filename) { - if (filename.length() && filename[0] == '@') { - /* Dummy, we don't use texture handles for builtin textures but need - * to tell the OSL runtime optimizer that this is a valid texture. */ + OSLTextureHandleMap::iterator it = osl_globals->textures.find(filename); + + /* For non-OIIO textures, just return a pointer to our own OSLTextureHandle. */ + if (it != osl_globals->textures.end()) { + if (it->second->type != OSLTextureHandle::OIIO) { + return (TextureSystem::TextureHandle *)it->second.get(); + } + } + + /* Get handle from OpenImageIO. */ + OSL::TextureSystem *ts = osl_ts; + TextureSystem::TextureHandle *handle = ts->get_texture_handle(filename); + if (handle == NULL) { return NULL; } - else { - return texturesys()->get_texture_handle(filename); + + /* Insert new OSLTextureHandle if needed. */ + if (it == osl_globals->textures.end()) { + osl_globals->textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::OIIO)); + it = osl_globals->textures.find(filename); } + + /* Assign OIIO texture handle and return. */ + it->second->oiio_handle = handle; + return (TextureSystem::TextureHandle *)it->second.get(); } bool OSLRenderServices::good(TextureSystem::TextureHandle *texture_handle) { - return texturesys()->good(texture_handle); + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + + if (handle->oiio_handle) { + OSL::TextureSystem *ts = osl_ts; + return ts->good(handle->oiio_handle); + } + else { + return true; + } } bool OSLRenderServices::texture(ustring filename, @@ -988,70 +1011,29 @@ bool OSLRenderServices::texture(ustring filename, float *dresultdt, ustring *errormessage) { - OSL::TextureSystem *ts = osl_ts; - ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = kernel_globals; - - if (texture_thread_info == NULL) { - OSLThreadData *tdata = kg->osl_tdata; - texture_thread_info = tdata->oiio_thread_info; - } - -#ifdef WITH_PTEX - /* todo: this is just a quick hack, only works with particular files and options */ - if (string_endswith(filename.string(), ".ptx")) { - float2 uv; - int faceid; - - if (!primitive_ptex(kg, sd, &uv, &faceid)) - return false; - - float u = uv.x; - float v = uv.y; - float dudx = 0.0f; - float dvdx = 0.0f; - float dudy = 0.0f; - float dvdy = 0.0f; - - Ptex::String error; - PtexPtr r(ptex_cache->get(filename.c_str(), error)); - - if (!r) { - // std::cerr << error.c_str() << std::endl; - return false; - } - - bool mipmaplerp = false; - float sharpness = 1.0f; - PtexFilter::Options opts(PtexFilter::f_bicubic, mipmaplerp, sharpness); - PtexPtr f(PtexFilter::getFilter(r, opts)); - - f->eval(result, options.firstchannel, nchannels, faceid, u, v, dudx, dvdx, dudy, dvdy); - - for (int c = r->numChannels(); c < nchannels; c++) - result[c] = result[0]; - - return true; - } -#endif + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO; bool status = false; - if (filename.length() && filename[0] == '@') { - if (filename == u_at_bevel) { + switch (texture_type) { + case OSLTextureHandle::BEVEL: { /* Bevel shader hack. */ if (nchannels >= 3) { + ShaderData *sd = (ShaderData *)(sg->renderstate); PathState *state = sd->osl_path_state; int num_samples = (int)s; float radius = t; - float3 N = svm_bevel(kg, sd, state, radius, num_samples); + float3 N = svm_bevel(kernel_globals, sd, state, radius, num_samples); result[0] = N.x; result[1] = N.y; result[2] = N.z; status = true; } + break; } - else if (filename == u_at_ao) { + case OSLTextureHandle::AO: { /* AO shader hack. */ + ShaderData *sd = (ShaderData *)(sg->renderstate); PathState *state = sd->osl_path_state; int num_samples = (int)s; float radius = t; @@ -1066,19 +1048,13 @@ bool OSLRenderServices::texture(ustring filename, if ((int)options.tblur) { flags |= NODE_AO_GLOBAL_RADIUS; } - result[0] = svm_ao(kg, sd, N, state, radius, num_samples, flags); + result[0] = svm_ao(kernel_globals, sd, N, state, radius, num_samples, flags); status = true; + break; } - else if (filename[1] == 'l') { - /* IES light. */ - int slot = atoi(filename.c_str() + 2); - result[0] = kernel_ies_interp(kg, slot, s, t); - status = true; - } - else { + case OSLTextureHandle::SVM: { /* Packed texture. */ - int slot = atoi(filename.c_str() + 2); - float4 rgba = kernel_tex_image_interp(kg, slot, s, 1.0f - t); + float4 rgba = kernel_tex_image_interp(kernel_globals, handle->svm_slot, s, 1.0f - t); result[0] = rgba[0]; if (nchannels > 1) @@ -1088,37 +1064,59 @@ bool OSLRenderServices::texture(ustring filename, if (nchannels > 3) result[3] = rgba[3]; status = true; + break; } - } - else { - if (texture_handle != NULL) { - status = ts->texture(texture_handle, - texture_thread_info, - options, - s, - t, - dsdx, - dtdx, - dsdy, - dtdy, - nchannels, - result, - dresultds, - dresultdt); + case OSLTextureHandle::IES: { + /* IES light. */ + result[0] = kernel_ies_interp(kernel_globals, handle->svm_slot, s, t); + status = true; + break; } - else { - status = ts->texture(filename, - options, - s, - t, - dsdx, - dtdx, - dsdy, - dtdy, - nchannels, - result, - dresultds, - dresultdt); + case OSLTextureHandle::OIIO: { + /* OpenImageIO texture cache. */ + OSL::TextureSystem *ts = osl_ts; + + if (handle && handle->oiio_handle) { + if (texture_thread_info == NULL) { + OSLThreadData *tdata = kernel_globals->osl_tdata; + texture_thread_info = tdata->oiio_thread_info; + } + + status = ts->texture(handle->oiio_handle, + texture_thread_info, + options, + s, + t, + dsdx, + dtdx, + dsdy, + dtdy, + nchannels, + result, + dresultds, + dresultdt); + } + else { + status = ts->texture(filename, + options, + s, + t, + dsdx, + dtdx, + dsdy, + dtdy, + nchannels, + result, + dresultds, + dresultdt); + } + + if (!status) { + /* This might be slow, but prevents error messages leak and + * other nasty stuff happening. */ + ts->geterror(); + } + break; } } @@ -1131,11 +1129,6 @@ bool OSLRenderServices::texture(ustring filename, if (nchannels == 4) result[3] = 1.0f; } - /* This might be slow, but prevents error messages leak and - * other nasty stuff happening. - */ - string err = ts->geterror(); - (void)err; } return status; @@ -1157,56 +1150,76 @@ bool OSLRenderServices::texture3d(ustring filename, float *dresultdr, ustring *errormessage) { - OSL::TextureSystem *ts = osl_ts; - ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = kernel_globals; + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO; + bool status = false; - if (texture_thread_info == NULL) { - OSLThreadData *tdata = kg->osl_tdata; - texture_thread_info = tdata->oiio_thread_info; - } + switch (texture_type) { + case OSLTextureHandle::SVM: { + /* Packed texture. */ + int slot = handle->svm_slot; + float4 rgba = kernel_tex_image_interp_3d( + kernel_globals, slot, P.x, P.y, P.z, INTERPOLATION_NONE); - bool status; - if (filename.length() && filename[0] == '@') { - int slot = atoi(filename.c_str() + 1); - float4 rgba = kernel_tex_image_interp_3d(kg, slot, P.x, P.y, P.z, INTERPOLATION_NONE); - - result[0] = rgba[0]; - if (nchannels > 1) - result[1] = rgba[1]; - if (nchannels > 2) - result[2] = rgba[2]; - if (nchannels > 3) - result[3] = rgba[3]; - status = true; - } - else { - if (texture_handle != NULL) { - status = ts->texture3d(texture_handle, - texture_thread_info, - options, - P, - dPdx, - dPdy, - dPdz, - nchannels, - result, - dresultds, - dresultdt, - dresultdr); + result[0] = rgba[0]; + if (nchannels > 1) + result[1] = rgba[1]; + if (nchannels > 2) + result[2] = rgba[2]; + if (nchannels > 3) + result[3] = rgba[3]; + status = true; + break; } - else { - status = ts->texture3d(filename, - options, - P, - dPdx, - dPdy, - dPdz, - nchannels, - result, - dresultds, - dresultdt, - dresultdr); + case OSLTextureHandle::OIIO: { + /* OpenImageIO texture cache. */ + OSL::TextureSystem *ts = osl_ts; + + if (handle && handle->oiio_handle) { + if (texture_thread_info == NULL) { + OSLThreadData *tdata = kernel_globals->osl_tdata; + texture_thread_info = tdata->oiio_thread_info; + } + + status = ts->texture3d(handle->oiio_handle, + texture_thread_info, + options, + P, + dPdx, + dPdy, + dPdz, + nchannels, + result, + dresultds, + dresultdt, + dresultdr); + } + else { + status = ts->texture3d(filename, + options, + P, + dPdx, + dPdy, + dPdz, + nchannels, + result, + dresultds, + dresultdt, + dresultdr); + } + + if (!status) { + /* This might be slow, but prevents error messages leak and + * other nasty stuff happening. */ + ts->geterror(); + } + break; + } + case OSLTextureHandle::IES: + case OSLTextureHandle::AO: + case OSLTextureHandle::BEVEL: { + status = false; + break; } } @@ -1219,18 +1232,13 @@ bool OSLRenderServices::texture3d(ustring filename, if (nchannels == 4) result[3] = 1.0f; } - /* This might be slow, but prevents error messages leak and - * other nasty stuff happening. - */ - string err = ts->geterror(); - (void)err; } return status; } bool OSLRenderServices::environment(ustring filename, - TextureHandle *th, + TextureHandle *texture_handle, TexturePerthread *thread_info, TextureOpt &options, OSL::ShaderGlobals *sg, @@ -1243,20 +1251,32 @@ bool OSLRenderServices::environment(ustring filename, float *dresultdt, ustring *errormessage) { + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; OSL::TextureSystem *ts = osl_ts; + bool status = false; - if (thread_info == NULL) { - OSLThreadData *tdata = kernel_globals->osl_tdata; - thread_info = tdata->oiio_thread_info; + if (handle && handle->oiio_handle) { + if (thread_info == NULL) { + OSLThreadData *tdata = kernel_globals->osl_tdata; + thread_info = tdata->oiio_thread_info; + } + + status = ts->environment(handle->oiio_handle, + thread_info, + options, + R, + dRdx, + dRdy, + nchannels, + result, + dresultds, + dresultdt); } - - if (th == NULL) { - th = ts->get_texture_handle(filename, thread_info); + else { + status = ts->environment( + filename, options, R, dRdx, dRdy, nchannels, result, dresultds, dresultdt); } - bool status = ts->environment( - th, thread_info, options, R, dRdx, dRdy, nchannels, result, dresultds, dresultdt); - if (!status) { if (nchannels == 3 || nchannels == 4) { result[0] = 1.0f; @@ -1273,20 +1293,22 @@ bool OSLRenderServices::environment(ustring filename, bool OSLRenderServices::get_texture_info(OSL::ShaderGlobals *sg, ustring filename, - TextureHandle *th, + TextureHandle *texture_handle, int subimage, ustring dataname, TypeDesc datatype, void *data) { + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + + /* No texture info for other texture types. */ + if (handle && handle->type != OSLTextureHandle::OIIO) { + return NULL; + } + + /* Get texture info from OpenImageIO. */ OSL::TextureSystem *ts = osl_ts; - if (filename.length() && filename[0] == '@') { - /* Special builtin textures. */ - return false; - } - else { - return ts->get_texture_info(filename, subimage, dataname, datatype, data); - } + return ts->get_texture_info(filename, subimage, dataname, datatype, data); } int OSLRenderServices::pointcloud_search(OSL::ShaderGlobals *sg, diff --git a/intern/cycles/kernel/shaders/node_ies_light.osl b/intern/cycles/kernel/shaders/node_ies_light.osl index ea8c44e09de..ce0173451da 100644 --- a/intern/cycles/kernel/shaders/node_ies_light.osl +++ b/intern/cycles/kernel/shaders/node_ies_light.osl @@ -21,7 +21,7 @@ shader node_ies_light(int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - int slot = 0, + string filename = "", float Strength = 1.0, point Vector = I, output float Fac = 0.0) @@ -37,5 +37,5 @@ shader node_ies_light(int use_mapping = 0, float v_angle = acos(-p[2]); float h_angle = atan2(p[0], p[1]) + M_PI; - Fac = Strength * texture(format("@l%d", slot), h_angle, v_angle); + Fac = Strength * texture(filename, h_angle, v_angle); } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 16416a9a009..35e9f8df5a8 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -374,18 +374,7 @@ void ImageTextureNode::compile(OSLCompiler &compiler) is_linear = metadata.is_linear; } - if (slot == -1) { - compiler.parameter(this, "filename"); - } - else { - /* TODO(sergey): It's not so simple to pass custom attribute - * to the texture() function in order to make builtin images - * support more clear. So we use special file name which is - * "@i" and check whether file name matches this - * mask in the OSLRenderServices::texture(). - */ - compiler.parameter("filename", string_printf("@i%d", slot).c_str()); - } + compiler.parameter_texture("filename", filename, slot); if (is_linear || color_space != NODE_COLOR_SPACE_COLOR) compiler.parameter("color_space", "linear"); else @@ -556,12 +545,7 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler) is_linear = metadata.is_linear; } - if (slot == -1) { - compiler.parameter(this, "filename"); - } - else { - compiler.parameter("filename", string_printf("@i%d", slot).c_str()); - } + compiler.parameter_texture("filename", filename, slot); compiler.parameter(this, "projection"); if (is_linear || color_space != NODE_COLOR_SPACE_COLOR) compiler.parameter("color_space", "linear"); @@ -1080,7 +1064,7 @@ void IESLightNode::compile(OSLCompiler &compiler) tex_mapping.compile(compiler); - compiler.parameter("slot", slot); + compiler.parameter_texture_ies("filename", slot); compiler.add(this, "node_ies_light"); } @@ -1567,9 +1551,7 @@ void PointDensityTextureNode::compile(OSLCompiler &compiler) if (use_density || use_color) { add_image(); - if (slot != -1) { - compiler.parameter("filename", string_printf("@i%d", slot).c_str()); - } + compiler.parameter_texture("filename", ustring(), slot); if (space == NODE_TEX_VOXEL_SPACE_WORLD) { compiler.parameter("mapping", tfm); compiler.parameter("use_mapping", 1); diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 24c5a599c1a..5ee453275b9 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -145,6 +145,10 @@ void OSLShaderManager::device_update(Device *device, /* set texture system */ scene->image_manager->set_osl_texture_system((void *)ts); + /* add special builtin texture types */ + og->textures.insert(ustring("@ao"), new OSLTextureHandle(OSLTextureHandle::AO)); + og->textures.insert(ustring("@bevel"), new OSLTextureHandle(OSLTextureHandle::BEVEL)); + device_update_common(device, dscene, scene, progress); { @@ -1201,6 +1205,30 @@ void OSLCompiler::compile(Scene *scene, Shader *shader) osl_globals->bump_state.push_back(shader->osl_surface_bump_ref); } +void OSLCompiler::parameter_texture(const char *name, ustring filename, int svm_slot) +{ + if (svm_slot != -1) { + /* It's not so simple to pass custom attribute to the texture() function + * in order to make builtin images support more clear. So we use special + * file name which is "@i" and use that for lookup in + * in OSLRenderServices::texture(). */ + filename = string_printf("@i%d", svm_slot).c_str(); + osl_globals->textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::SVM, svm_slot)); + } + else { + osl_globals->textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::OIIO)); + } + + parameter(name, filename); +} + +void OSLCompiler::parameter_texture_ies(const char *name, int svm_slot) +{ + ustring filename(string_printf("@l%d", svm_slot).c_str()); + osl_globals->textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::IES, svm_slot)); + parameter(name, filename); +} + #else void OSLCompiler::add(ShaderNode * /*node*/, const char * /*name*/, bool /*isfilepath*/) @@ -1255,6 +1283,16 @@ void OSLCompiler::parameter_color_array(const char * /*name*/, const array T *util_aligned_new() +{ + void *mem = util_aligned_malloc(sizeof(T), alignof(T)); + return new (mem) T(); +} + CCL_NAMESPACE_END #endif /* __UTIL_ALIGNED_MALLOC_H__ */