From 9800837b987930e6152c2dc27cae5bd55873d306 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 15 Feb 2019 08:18:38 +0100 Subject: Cycles: Support multithreaded compilation of kernels This patch implements a workaround to get the multithreaded compilation from D2231 working. So far, it only works for Blender, not for Cycles Standalone. Also, I have only tested the Linux codepath in the helper function. Depends on D2231. Patch by lukasstockner97, jbakker, brecht job | scene_name | compilation_time ----------+-----------------+------------------ Baseline | empty | 22.73 D2264 | empty | 13.94 Baseline | bmw | 56.44 D2264 | bmw | 41.32 Baseline | fishycat | 59.50 D2264 | fishycat | 45.19 Baseline | barbershop | 212.28 D2264 | barbershop | 169.81 Baseline | victor | 67.51 D2264 | victor | 53.60 Baseline | classroom | 51.46 D2264 | classroom | 39.02 Baseline | koro | 62.48 D2264 | koro | 49.03 Baseline | pavillion | 54.37 D2264 | pavillion | 38.82 Baseline | splash279 | 47.43 D2264 | splash279 | 37.94 Baseline | volume_emission | 145.22 D2264 | volume_emission | 121.10 This patch reduced compilation time as the split kernels and base kernels are compiled in parallel. In cycles debug mode (256) you can set unmark the opencl single program file, what reduces the compilation time even further (bmw 17 seconds, barbershop 53 seconds). Reviewers: brecht, dingto, sergey, juicyfruit, lukasstockner97 Reviewed By: brecht Subscribers: Loner, jbakker, candreacchio, 3dLuver, LazyDodo, bliblubli Differential Revision: https://developer.blender.org/D2264 --- intern/cycles/blender/CMakeLists.txt | 4 + intern/cycles/blender/blender_python.cpp | 30 ++++ intern/cycles/device/device_intern.h | 1 + intern/cycles/device/opencl/opencl.h | 20 ++- intern/cycles/device/opencl/opencl_base.cpp | 20 +-- intern/cycles/device/opencl/opencl_mega.cpp | 24 ++- intern/cycles/device/opencl/opencl_split.cpp | 171 +++++++++++++++++++-- intern/cycles/device/opencl/opencl_util.cpp | 140 +++++++++++++++-- intern/cycles/kernel/CMakeLists.txt | 1 + .../kernel/kernels/opencl/kernel_split_bundle.cl | 28 ++++ intern/cycles/util/util_system.cpp | 22 +++ intern/cycles/util/util_system.h | 4 + 12 files changed, 420 insertions(+), 45 deletions(-) create mode 100644 intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 84e2690333e..f8720de366f 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -51,6 +51,10 @@ set(ADDON_FILES add_definitions(${GL_DEFINITIONS}) +if(WITH_CYCLES_DEVICE_OPENCL) + add_definitions(-DWITH_OPENCL) +endif() + if(WITH_CYCLES_NETWORK) add_definitions(-DWITH_NETWORK) endif() diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index bf7605ed5b1..513941b1fcc 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -40,6 +40,10 @@ #include #endif +#ifdef WITH_OPENCL +#include "device/device_intern.h" +#endif + CCL_NAMESPACE_BEGIN namespace { @@ -624,6 +628,31 @@ static PyObject *opencl_disable_func(PyObject * /*self*/, PyObject * /*value*/) DebugFlags().opencl.device_type = DebugFlags::OpenCL::DEVICE_NONE; Py_RETURN_NONE; } + +static PyObject *opencl_compile_func(PyObject * /*self*/, PyObject *args) +{ + PyObject *sequence = PySequence_Fast(args, "Arguments must be a sequence"); + if(sequence == NULL) { + Py_RETURN_FALSE; + } + + vector parameters; + for(Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(sequence); i++) { + PyObject *item = PySequence_Fast_GET_ITEM(sequence, i); + PyObject *item_as_string = PyObject_Str(item); + const char *parameter_string = PyUnicode_AsUTF8(item_as_string); + parameters.push_back(parameter_string); + Py_DECREF(item_as_string); + } + Py_DECREF(sequence); + + if (device_opencl_compile_kernel(parameters)) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } +} #endif static bool denoise_parse_filepaths(PyObject *pyfilepaths, vector& filepaths) @@ -899,6 +928,7 @@ static PyMethodDef methods[] = { {"system_info", system_info_func, METH_NOARGS, ""}, #ifdef WITH_OPENCL {"opencl_disable", opencl_disable_func, METH_NOARGS, ""}, + {"opencl_compile", opencl_compile_func, METH_VARARGS, ""}, #endif /* Standalone denoising */ diff --git a/intern/cycles/device/device_intern.h b/intern/cycles/device/device_intern.h index 0b26057c3ba..94df1e009eb 100644 --- a/intern/cycles/device/device_intern.h +++ b/intern/cycles/device/device_intern.h @@ -24,6 +24,7 @@ class Device; Device *device_cpu_create(DeviceInfo& info, Stats &stats, Profiler &profiler, bool background); bool device_opencl_init(); Device *device_opencl_create(DeviceInfo& info, Stats &stats, Profiler &profiler, bool background); +bool device_opencl_compile_kernel(const vector& parameters); bool device_cuda_init(); Device *device_cuda_create(DeviceInfo& info, Stats &stats, Profiler &profiler, bool background); Device *device_network_create(DeviceInfo& info, Stats &stats, Profiler &profiler, const char *address); diff --git a/intern/cycles/device/opencl/opencl.h b/intern/cycles/device/opencl/opencl.h index 9b763167459..a2c0e53b3e7 100644 --- a/intern/cycles/device/opencl/opencl.h +++ b/intern/cycles/device/opencl/opencl.h @@ -268,6 +268,7 @@ public: cl_platform_id cpPlatform; cl_device_id cdDevice; cl_int ciErr; + int device_num; class OpenCLProgram { public: @@ -293,7 +294,15 @@ public: private: bool build_kernel(const string *debug_src); + /* Build the program by calling the own process. + * This is required for multithreaded OpenCL compilation, since most Frameworks serialize + * build calls internally if they come from the same process. + * If that is not supported, this function just returns false. + */ + bool compile_separate(const string& clbin); + /* Build the program by calling OpenCL directly. */ bool compile_kernel(const string *debug_src); + /* Loading and saving the program from/to disk. */ bool load_binary(const string& clbin, const string *debug_src = NULL); bool save_binary(const string& clbin); @@ -342,12 +351,17 @@ public: bool opencl_version_check(); string device_md5_hash(string kernel_custom_build_options = ""); - bool load_kernels(const DeviceRequestedFeatures& requested_features); + virtual bool load_kernels(const DeviceRequestedFeatures& requested_features); /* Has to be implemented by the real device classes. * The base device will then load all these programs. */ - virtual bool load_kernels(const DeviceRequestedFeatures& requested_features, - vector &programs) = 0; + virtual bool add_kernel_programs(const DeviceRequestedFeatures& requested_features, + vector &programs) = 0; + + /* Get the name of the opencl program for the given kernel */ + virtual const string get_opencl_program_name(bool single_program, const string& kernel_name) = 0; + /* Get the program file name to compile (*.cl) for the given kernel */ + virtual const string get_opencl_program_filename(bool single_program, const string& kernel_name) = 0; void mem_alloc(device_memory& mem); void mem_copy_to(device_memory& mem); diff --git a/intern/cycles/device/opencl/opencl_base.cpp b/intern/cycles/device/opencl/opencl_base.cpp index 4417065bb7f..d8f9a242ac8 100644 --- a/intern/cycles/device/opencl/opencl_base.cpp +++ b/intern/cycles/device/opencl/opencl_base.cpp @@ -93,6 +93,7 @@ OpenCLDeviceBase::OpenCLDeviceBase(DeviceInfo& info, Stats &stats, Profiler &pro } assert(info.num < usable_devices.size()); OpenCLPlatformDevice& platform_device = usable_devices[info.num]; + device_num = info.num; cpPlatform = platform_device.platform_id; cdDevice = platform_device.device_id; platform_name = platform_device.platform_name; @@ -143,7 +144,6 @@ OpenCLDeviceBase::OpenCLDeviceBase(DeviceInfo& info, Stats &stats, Profiler &pro texture_info.resize(1); memory_manager.alloc("texture_info", texture_info); - fprintf(stderr, "Device init success\n"); device_initialized = true; } @@ -251,15 +251,13 @@ bool OpenCLDeviceBase::load_kernels(const DeviceRequestedFeatures& requested_fea programs.push_back(&base_program); programs.push_back(&denoising_program); /* Call actual class to fill the vector with its programs. */ - if(!load_kernels(requested_features, programs)) { + if(!add_kernel_programs(requested_features, programs)) { return false; } - /* Parallel compilation is supported by Cycles, but currently all OpenCL frameworks - * serialize the calls internally, so it's not much use right now. - * Note: When enabling parallel compilation, use_stdout in the OpenCLProgram constructor - * should be set to false as well. */ -#if 0 + /* Parallel compilation of Cycles kernels, this launches multiple + * processes to workaround OpenCL frameworks serializing the calls + * internally within a single process. */ TaskPool task_pool; foreach(OpenCLProgram *program, programs) { task_pool.push(function_bind(&OpenCLProgram::load, program)); @@ -273,14 +271,6 @@ bool OpenCLDeviceBase::load_kernels(const DeviceRequestedFeatures& requested_fea return false; } } -#else - foreach(OpenCLProgram *program, programs) { - program->load(); - if(!program->is_loaded()) { - return false; - } - } -#endif return true; } diff --git a/intern/cycles/device/opencl/opencl_mega.cpp b/intern/cycles/device/opencl/opencl_mega.cpp index 0a7bf96fed7..c0b9e81d4d3 100644 --- a/intern/cycles/device/opencl/opencl_mega.cpp +++ b/intern/cycles/device/opencl/opencl_mega.cpp @@ -35,19 +35,35 @@ public: OpenCLDeviceMegaKernel(DeviceInfo& info, Stats &stats, Profiler &profiler, bool background_) : OpenCLDeviceBase(info, stats, profiler, background_), - path_trace_program(this, "megakernel", "kernel.cl", "-D__COMPILE_ONLY_MEGAKERNEL__ ") + path_trace_program(this, + get_opencl_program_name(false, "megakernel"), + get_opencl_program_filename(false, "megakernel"), + "-D__COMPILE_ONLY_MEGAKERNEL__ ") { } - virtual bool show_samples() const { + + virtual bool show_samples() const + { return true; } - virtual BVHLayoutMask get_bvh_layout_mask() const { + virtual BVHLayoutMask get_bvh_layout_mask() const + { return BVH_LAYOUT_BVH2; } - virtual bool load_kernels(const DeviceRequestedFeatures& /*requested_features*/, + const string get_opencl_program_name(bool /*single_program*/, const string& kernel_name) + { + return kernel_name; + } + + const string get_opencl_program_filename(bool /*single_program*/, const string& /*kernel_name*/) + { + return "kernel.cl"; + } + + virtual bool add_kernel_programs(const DeviceRequestedFeatures& /*requested_features*/, vector &programs) { path_trace_program.add_kernel(ustring("path_trace")); diff --git a/intern/cycles/device/opencl/opencl_split.cpp b/intern/cycles/device/opencl/opencl_split.cpp index 5a2555f9f80..b759f69d3ab 100644 --- a/intern/cycles/device/opencl/opencl_split.cpp +++ b/intern/cycles/device/opencl/opencl_split.cpp @@ -79,6 +79,27 @@ public: OpenCLProgram program_data_init; OpenCLProgram program_state_buffer_size; + OpenCLProgram program_split; + + OpenCLProgram program_path_init; + OpenCLProgram program_scene_intersect; + OpenCLProgram program_lamp_emission; + OpenCLProgram program_do_volume; + OpenCLProgram program_queue_enqueue; + OpenCLProgram program_indirect_background; + OpenCLProgram program_shader_setup; + OpenCLProgram program_shader_sort; + OpenCLProgram program_shader_eval; + OpenCLProgram program_holdout_emission_blurring_pathtermination_ao; + OpenCLProgram program_subsurface_scatter; + OpenCLProgram program_direct_lighting; + OpenCLProgram program_shadow_blocked_ao; + OpenCLProgram program_shadow_blocked_dl; + OpenCLProgram program_enqueue_inactive; + OpenCLProgram program_next_iteration_setup; + OpenCLProgram program_indirect_subsurface; + OpenCLProgram program_buffer_update; + OpenCLDeviceSplitKernel(DeviceInfo& info, Stats &stats, Profiler &profiler, bool background_); ~OpenCLDeviceSplitKernel() @@ -99,26 +120,150 @@ public: return BVH_LAYOUT_BVH2; } - virtual bool load_kernels(const DeviceRequestedFeatures& requested_features, + virtual bool load_kernels(const DeviceRequestedFeatures& requested_features) + { + if (!OpenCLDeviceBase::load_kernels(requested_features)) { + return false; + } + return split_kernel->load_kernels(requested_features); + } + + const string fast_compiled_kernels = + "path_init " + "scene_intersect " + "queue_enqueue " + "shader_setup " + "shader_sort " + "enqueue_inactive " + "next_iteration_setup " + "indirect_subsurface " + "buffer_update"; + + const string get_opencl_program_name(bool single_program, const string& kernel_name) + { + if (single_program) { + return "split"; + } + else { + if (fast_compiled_kernels.find(kernel_name) != std::string::npos) { + return "split_bundle"; + } + else { + return "split_" + kernel_name; + } + } + } + + const string get_opencl_program_filename(bool single_program, const string& kernel_name) + { + if (single_program) { + return "kernel_split.cl"; + } + else { + if (fast_compiled_kernels.find(kernel_name) != std::string::npos) { + return "kernel_split_bundle.cl"; + } + else { + return "kernel_" + kernel_name + ".cl"; + } + } + } + + virtual bool add_kernel_programs(const DeviceRequestedFeatures& requested_features, vector &programs) { bool single_program = OpenCLInfo::use_single_program(); - program_data_init = OpenCLDeviceBase::OpenCLProgram(this, - single_program ? "split" : "split_data_init", - single_program ? "kernel_split.cl" : "kernel_data_init.cl", - get_build_options(this, requested_features)); - + program_data_init = OpenCLDeviceBase::OpenCLProgram( + this, + get_opencl_program_name(single_program, "data_init"), + get_opencl_program_filename(single_program, "data_init"), + get_build_options(this, requested_features)); program_data_init.add_kernel(ustring("path_trace_data_init")); programs.push_back(&program_data_init); - program_state_buffer_size = OpenCLDeviceBase::OpenCLProgram(this, - single_program ? "split" : "split_state_buffer_size", - single_program ? "kernel_split.cl" : "kernel_state_buffer_size.cl", - get_build_options(this, requested_features)); + program_state_buffer_size = OpenCLDeviceBase::OpenCLProgram( + this, + get_opencl_program_name(single_program, "state_buffer_size"), + get_opencl_program_filename(single_program, "state_buffer_size"), + get_build_options(this, requested_features)); + program_state_buffer_size.add_kernel(ustring("path_trace_state_buffer_size")); programs.push_back(&program_state_buffer_size); - return split_kernel->load_kernels(requested_features); + +#define ADD_SPLIT_KERNEL_SINGLE_PROGRAM(kernel_name) program_split.add_kernel(ustring("path_trace_"#kernel_name)); +#define ADD_SPLIT_KERNEL_SPLIT_PROGRAM(kernel_name) \ + program_##kernel_name = \ + OpenCLDeviceBase::OpenCLProgram(this, \ + "split_"#kernel_name, \ + "kernel_"#kernel_name".cl", \ + get_build_options(this, requested_features)); \ + program_##kernel_name.add_kernel(ustring("path_trace_"#kernel_name)); \ + programs.push_back(&program_##kernel_name); + + if (single_program) { + program_split = OpenCLDeviceBase::OpenCLProgram( + this, + "split" , + "kernel_split.cl", + get_build_options(this, requested_features)); + + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(path_init); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(scene_intersect); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(lamp_emission); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(do_volume); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(queue_enqueue); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(indirect_background); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(shader_setup); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(shader_sort); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(shader_eval); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(holdout_emission_blurring_pathtermination_ao); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(subsurface_scatter); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(direct_lighting); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(shadow_blocked_ao); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(shadow_blocked_dl); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(enqueue_inactive); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(next_iteration_setup); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(indirect_subsurface); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(buffer_update); + + programs.push_back(&program_split); + } + else { + /* Ordered with most complex kernels first, to reduce overall compile time. */ + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(subsurface_scatter); + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(do_volume); + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(shadow_blocked_dl); + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(shadow_blocked_ao); + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(holdout_emission_blurring_pathtermination_ao); + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(lamp_emission); + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(direct_lighting); + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(indirect_background); + ADD_SPLIT_KERNEL_SPLIT_PROGRAM(shader_eval); + + /* Quick kernels bundled in a single program to reduce overhead of starting + * Blender processes. */ + program_split = OpenCLDeviceBase::OpenCLProgram( + this, + "split_bundle" , + "kernel_split_bundle.cl", + get_build_options(this, requested_features)); + + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(path_init); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(scene_intersect); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(queue_enqueue); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(shader_setup); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(shader_sort); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(enqueue_inactive); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(next_iteration_setup); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(indirect_subsurface); + ADD_SPLIT_KERNEL_SINGLE_PROGRAM(buffer_update); + programs.push_back(&program_split); + } +#undef ADD_SPLIT_KERNEL_SPLIT_PROGRAM +#undef ADD_SPLIT_KERNEL_SINGLE_PROGRAM + + return true; } void thread_run(DeviceTask *task) @@ -281,8 +426,8 @@ public: bool single_program = OpenCLInfo::use_single_program(); kernel->program = OpenCLDeviceBase::OpenCLProgram(device, - single_program ? "split" : "split_" + kernel_name, - single_program ? "kernel_split.cl" : "kernel_" + kernel_name + ".cl", + device->get_opencl_program_name(single_program, kernel_name), + device->get_opencl_program_filename(single_program, kernel_name), get_build_options(device, requested_features)); kernel->program.add_kernel(ustring("path_trace_" + kernel_name)); diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp index f43aa5f350a..fe5ba4886a9 100644 --- a/intern/cycles/device/opencl/opencl_util.cpp +++ b/intern/cycles/device/opencl/opencl_util.cpp @@ -17,12 +17,14 @@ #ifdef WITH_OPENCL #include "device/opencl/opencl.h" +#include "device/device_intern.h" #include "util/util_debug.h" #include "util/util_logging.h" #include "util/util_md5.h" #include "util/util_path.h" #include "util/util_time.h" +#include "util/util_system.h" using std::cerr; using std::endl; @@ -369,17 +371,119 @@ bool OpenCLDeviceBase::OpenCLProgram::compile_kernel(const string *debug_src) } double starttime = time_dt(); - add_log(string("Compiling OpenCL program ") + program_name.c_str(), false); + add_log(string("Cycles: compiling OpenCL program ") + program_name + "...", false); add_log(string("Build flags: ") + kernel_build_options, true); if(!build_kernel(debug_src)) return false; - add_log(string("Kernel compilation of ") + program_name + " finished in " + string_printf("%.2lfs.\n", time_dt() - starttime), false); + double elapsed = time_dt() - starttime; + add_log(string_printf("Kernel compilation of %s finished in %.2lfs.", program_name.c_str(), elapsed), false); return true; } +bool OpenCLDeviceBase::OpenCLProgram::compile_separate(const string& clbin) +{ + vector args; + args.push_back("--background"); + args.push_back("--factory-startup"); + args.push_back("--python-expr"); + + args.push_back( + string_printf( + "import _cycles; _cycles.opencl_compile('%s', '%d', '%s', '%s', '%s', '%s', '%s')", + (DebugFlags().opencl.kernel_type != DebugFlags::OpenCL::KERNEL_DEFAULT)? "true" : "false", + device->device_num, + device->device_name.c_str(), + device->platform_name.c_str(), + (device->kernel_build_options(NULL) + kernel_build_options).c_str(), + kernel_file.c_str(), + clbin.c_str())); + + double starttime = time_dt(); + add_log(string("Cycles: compiling OpenCL program ") + program_name + "...", false); + add_log(string("Build flags: ") + kernel_build_options, true); + if(!system_call_self(args) || !path_exists(clbin)) { + return false; + } + + double elapsed = time_dt() - starttime; + add_log(string_printf("Kernel compilation of %s finished in %.2lfs.", program_name.c_str(), elapsed), false); + + return load_binary(clbin); +} + +/* Compile opencl kernel. This method is called from the _cycles Python + * module compile kernels. Parameters must match function above. */ +bool device_opencl_compile_kernel(const vector& parameters) +{ + bool force_all_platforms = parameters[0] == "true"; + int device_platform_id = std::stoi(parameters[1]); + const string& device_name = parameters[2]; + const string& platform_name = parameters[3]; + const string& build_options = parameters[4]; + const string& kernel_file = parameters[5]; + const string& binary_path = parameters[6]; + + if(clewInit() != CLEW_SUCCESS) { + return false; + } + + vector usable_devices; + OpenCLInfo::get_usable_devices(&usable_devices, force_all_platforms); + if(device_platform_id >= usable_devices.size()) { + return false; + } + + OpenCLPlatformDevice& platform_device = usable_devices[device_platform_id]; + if(platform_device.platform_name != platform_name || + platform_device.device_name != device_name) + { + return false; + } + + cl_platform_id platform = platform_device.platform_id; + cl_device_id device = platform_device.device_id; + const cl_context_properties context_props[] = { + CL_CONTEXT_PLATFORM, (cl_context_properties) platform, + 0, 0 + }; + + cl_int err; + cl_context context = clCreateContext(context_props, 1, &device, NULL, NULL, &err); + if(err != CL_SUCCESS) { + return false; + } + + string source = "#include \"kernel/kernels/opencl/" + kernel_file + "\" // " + path_files_md5_hash(path_get("kernel")) + "\n"; + source = path_source_replace_includes(source, path_get("source")); + size_t source_len = source.size(); + const char *source_str = source.c_str(); + cl_program program = clCreateProgramWithSource(context, 1, &source_str, &source_len, &err); + bool result = false; + + if(err == CL_SUCCESS) { + err = clBuildProgram(program, 0, NULL, build_options.c_str(), NULL, NULL); + + if(err == CL_SUCCESS) { + size_t size = 0; + clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL); + if(size > 0) { + vector binary(size); + uint8_t *bytes = &binary[0]; + clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(uint8_t*), &bytes, NULL); + result = path_write_binary(binary_path, binary); + } + } + clReleaseProgram(program); + } + + clReleaseContext(context); + + return result; +} + bool OpenCLDeviceBase::OpenCLProgram::load_binary(const string& clbin, const string *debug_src) { @@ -467,15 +571,31 @@ void OpenCLDeviceBase::OpenCLProgram::load() } else { add_log(string("Kernel file ") + clbin + " either doesn't exist or failed to be loaded by driver.", true); - - /* If does not exist or loading binary failed, compile kernel. */ - if(!compile_kernel(debug_src)) { - return; + if(!path_exists(clbin)) { + if(compile_separate(clbin)) { + add_log(string("Built and loaded program from ") + clbin + ".", true); + loaded = true; + } + else { + add_log(string("Separate-process building of ") + clbin + " failed, will fall back to regular building.", true); + + /* If does not exist or loading binary failed, compile kernel. */ + if(!compile_kernel(debug_src)) { + return; + } + + /* Save binary for reuse. */ + if(!save_binary(clbin)) { + add_log(string("Saving compiled OpenCL kernel to ") + clbin + " failed!", true); + } + } } - - /* Save binary for reuse. */ - if(!save_binary(clbin)) { - add_log(string("Saving compiled OpenCL kernel to ") + clbin + " failed!", true); + else { + add_log(string("Kernel file ") + clbin + "exists, but failed to be loaded by driver.", true); + /* Fall back to compiling. */ + if(!compile_kernel(debug_src)) { + return; + } } } diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 163aacf19f9..f7041ee2783 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC_OPENCL_KERNELS kernels/opencl/kernel.cl kernels/opencl/kernel_state_buffer_size.cl kernels/opencl/kernel_split.cl + kernels/opencl/kernel_split_bundle.cl kernels/opencl/kernel_data_init.cl kernels/opencl/kernel_path_init.cl kernels/opencl/kernel_queue_enqueue.cl diff --git a/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl b/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl new file mode 100644 index 00000000000..71ea68382b4 --- /dev/null +++ b/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl @@ -0,0 +1,28 @@ +/* + * Copyright 2011-2017 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kernel/kernel_compat_opencl.h" // PRECOMPILED +#include "kernel/split/kernel_split_common.h" // PRECOMPILED + +#include "kernel/kernels/opencl/kernel_path_init.cl" +#include "kernel/kernels/opencl/kernel_scene_intersect.cl" +#include "kernel/kernels/opencl/kernel_queue_enqueue.cl" +#include "kernel/kernels/opencl/kernel_shader_setup.cl" +#include "kernel/kernels/opencl/kernel_shader_sort.cl" +#include "kernel/kernels/opencl/kernel_enqueue_inactive.cl" +#include "kernel/kernels/opencl/kernel_next_iteration_setup.cl" +#include "kernel/kernels/opencl/kernel_indirect_subsurface.cl" +#include "kernel/kernels/opencl/kernel_buffer_update.cl" diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/util_system.cpp index fc6db1f6662..a79829a3dd9 100644 --- a/intern/cycles/util/util_system.cpp +++ b/intern/cycles/util/util_system.cpp @@ -22,6 +22,9 @@ #include +#include +OIIO_NAMESPACE_USING + #ifdef _WIN32 # if(!defined(FREE_WINDOWS)) # include @@ -329,6 +332,25 @@ bool system_cpu_support_avx2() #endif +bool system_call_self(const vector& args) +{ + /* Escape program and arguments in case they contain spaces. */ + string cmd = "\"" + Sysutil::this_program_path() + "\""; + + for(int i = 0; i < args.size(); i++) { + cmd += " \"" + args[i] + "\""; + } + + /* Quiet output. */ +#ifdef _WIN32 + cmd += " > nul"; +#else + cmd += " > /dev/null"; +#endif + + return (system(cmd.c_str()) == 0); +} + size_t system_physical_ram() { #ifdef _WIN32 diff --git a/intern/cycles/util/util_system.h b/intern/cycles/util/util_system.h index 1e7cf1d9f2a..2590b31a59d 100644 --- a/intern/cycles/util/util_system.h +++ b/intern/cycles/util/util_system.h @@ -18,6 +18,7 @@ #define __UTIL_SYSTEM_H__ #include "util/util_string.h" +#include "util/util_vector.h" CCL_NAMESPACE_BEGIN @@ -61,6 +62,9 @@ bool system_cpu_support_avx2(); size_t system_physical_ram(); +/* Start a new process of the current application with the given arguments. */ +bool system_call_self(const vector& args); + CCL_NAMESPACE_END #endif /* __UTIL_SYSTEM_H__ */ -- cgit v1.2.3 From b42892931944898cfb1f9f32cc3b75e70a385676 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Sat, 16 Feb 2019 11:36:19 -0700 Subject: Windows: Fix building with llvm/clang 7.0.1 For llvm 6 the visual studio integration was 'not great' and we had our own, which broke when llvm 7.0.1 came out. llvm now has properly supported integration available on the VS market place hence we can retire our custom support. --- build_files/windows/configure_msbuild.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_files/windows/configure_msbuild.cmd b/build_files/windows/configure_msbuild.cmd index bdd4ecc227a..28d9950cb11 100644 --- a/build_files/windows/configure_msbuild.cmd +++ b/build_files/windows/configure_msbuild.cmd @@ -9,7 +9,7 @@ if "%BUILD_ARCH%"=="x64" ( ) if "%WITH_CLANG%"=="1" ( - set CLANG_CMAKE_ARGS=-T"LLVM-vs2017" + set CLANG_CMAKE_ARGS=-T"llvm" if "%WITH_ASAN%"=="1" ( set ASAN_CMAKE_ARGS=-DWITH_COMPILER_ASAN=On ) -- cgit v1.2.3 From d8293fd6be0c3a5a0e615bfd4717ecc36e19bd49 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Sat, 16 Feb 2019 11:42:55 -0700 Subject: Blenlib/Windows: Fix Build error with clang. __cpuid comes from intrin.h which was implicitly included somewhere for msvc builds, but not for clang. --- source/blender/blenlib/intern/system.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 7a1ad41caa6..d6c3ee9f8fa 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -31,6 +31,7 @@ #if defined(__linux__) || defined(__APPLE__) # include #elif defined(WIN32) +# include # include # include #endif -- cgit v1.2.3 From 8a97b8555587d8241fd79d54c08c1a6ced7acf53 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 17 Feb 2019 12:27:07 +0100 Subject: Fix T54504: Cycles wrong backwards compatibility with linked libraries. The code assumed all datablocks were read from .blend files saved with the same version. This restructures the Cycles versioning code to take into account libraries. --- intern/cycles/blender/addon/version_update.py | 450 ++++++++++++-------------- source/blender/makesrna/intern/rna_ID.c | 14 + 2 files changed, 213 insertions(+), 251 deletions(-) diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index 3a4b13ef9d2..f3d60d62e02 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -22,89 +22,39 @@ import math from bpy.app.handlers import persistent -def check_is_new_shading_ntree(node_tree): - for node in node_tree.nodes: - # If material has any node with ONLY new shading system - # compatibility then it's considered a Cycles material - # and versioning code would need to perform on it. - # - # We can not check for whether NEW_SHADING in compatibility - # because some nodes could have compatibility with both old - # and new shading system and they can't be used for any - # decision here. - if node.shading_compatibility == {'NEW_SHADING'}: - return True - - # If node is only compatible with old shading system - # then material can not be Cycles material and we - # can stopiterating nodes now. - if node.shading_compatibility == {'OLD_SHADING'}: - return False - return False - - -def check_is_new_shading_material(material): - if not material.node_tree: - return False - return check_is_new_shading_ntree(material.node_tree) - - -def check_is_new_shading_world(world): - if not world.node_tree: - return False - return check_is_new_shading_ntree(world.node_tree) - - -def check_is_new_shading_lamp(lamp): - if not lamp.node_tree: - return False - return check_is_new_shading_ntree(lamp.node_tree) - - -def foreach_notree_node(nodetree, callback, traversed): - if nodetree in traversed: - return - traversed.add(nodetree) +def foreach_cycles_nodetree_group(nodetree, traversed): for node in nodetree.nodes: - callback(node) if node.bl_idname == 'ShaderNodeGroup': - foreach_notree_node(node.node_tree, callback, traversed) + group = node.node_tree + if group and group not in traversed: + traversed.add(group) + yield group, group.library + yield from foreach_cycles_nodetree_group(group, traversed) -def foreach_cycles_node(callback): +def foreach_cycles_nodetree(): traversed = set() + for material in bpy.data.materials: - if check_is_new_shading_material(material): - foreach_notree_node( - material.node_tree, - callback, - traversed, - ) + nodetree = material.node_tree + if nodetree: + yield nodetree, material.library + yield from foreach_cycles_nodetree_group(nodetree, traversed) + for world in bpy.data.worlds: - if check_is_new_shading_world(world): - foreach_notree_node( - world.node_tree, - callback, - traversed, - ) - for lamp in bpy.data.lamps: - if check_is_new_shading_world(lamp): - foreach_notree_node( - lamp.node_tree, - callback, - traversed, - ) + nodetree = world.node_tree + if nodetree: + yield nodetree, world.library + foreach_cycles_nodetree_group(nodetree, traversed) + for lamp in bpy.data.lamps: + nodetree = lamp.node_tree + if nodetree: + yield nodetree, lamp.library + foreach_cycles_nodetree_group(nodetree, traversed) -def displacement_node_insert(material, nodetree, traversed): - if nodetree in traversed: - return - traversed.add(nodetree) - - for node in nodetree.nodes: - if node.bl_idname == 'ShaderNodeGroup': - displacement_node_insert(material, node.node_tree, traversed) +def displacement_node_insert(nodetree): # Gather links to replace displacement_links = [] for link in nodetree.links: @@ -134,12 +84,6 @@ def displacement_node_insert(material, nodetree, traversed): nodetree.links.new(node.outputs['Displacement'], to_socket) -def displacement_nodes_insert(): - traversed = set() - for material in bpy.data.materials: - if check_is_new_shading_material(material): - displacement_node_insert(material, material.node_tree, traversed) - def displacement_principled_nodes(node): if node.bl_idname == 'ShaderNodeDisplacement': @@ -150,11 +94,7 @@ def displacement_principled_nodes(node): node.subsurface_method = 'BURLEY' -def square_roughness_node_insert(material, nodetree, traversed): - if nodetree in traversed: - return - traversed.add(nodetree) - +def square_roughness_node_insert(nodetree): roughness_node_types = { 'ShaderNodeBsdfAnisotropic', 'ShaderNodeBsdfGlass', @@ -163,9 +103,7 @@ def square_roughness_node_insert(material, nodetree, traversed): # Update default values for node in nodetree.nodes: - if node.bl_idname == 'ShaderNodeGroup': - square_roughness_node_insert(material, node.node_tree, traversed) - elif node.bl_idname in roughness_node_types: + if node.bl_idname in roughness_node_types: roughness_input = node.inputs['Roughness'] roughness_input.default_value = math.sqrt(max(roughness_input.default_value, 0.0)) @@ -195,13 +133,6 @@ def square_roughness_node_insert(material, nodetree, traversed): nodetree.links.new(node.outputs['Value'], to_socket) -def square_roughness_nodes_insert(): - traversed = set() - for material in bpy.data.materials: - if check_is_new_shading_material(material): - square_roughness_node_insert(material, material.node_tree, traversed) - - def mapping_node_order_flip(node): """ Flip euler order of mapping shader node @@ -283,18 +214,12 @@ def custom_bake_remap(scene): scene.render.bake.use_pass_indirect = False -def ambient_occlusion_node_relink(material, nodetree, traversed): - if nodetree in traversed: - return - traversed.add(nodetree) - +def ambient_occlusion_node_relink(nodetree): for node in nodetree.nodes: if node.bl_idname == 'ShaderNodeAmbientOcclusion': node.samples = 1 node.only_local = False node.inputs['Distance'].default_value = 0.0 - elif node.bl_idname == 'ShaderNodeGroup': - ambient_occlusion_node_relink(material, node.node_tree, traversed) # Gather links to replace ao_links = [] @@ -311,13 +236,6 @@ def ambient_occlusion_node_relink(material, nodetree, traversed): nodetree.links.new(from_node.outputs['Color'], to_socket) -def ambient_occlusion_nodes_relink(): - traversed = set() - for material in bpy.data.materials: - if check_is_new_shading_material(material): - ambient_occlusion_node_relink(material, material.node_tree, traversed) - - @persistent def do_versions(self): if bpy.context.user_preferences.version <= (2, 78, 1): @@ -343,156 +261,186 @@ def do_versions(self): if not bpy.data.is_saved: return - # Clamp Direct/Indirect separation in 270 - if bpy.data.version <= (2, 70, 0): - for scene in bpy.data.scenes: - cscene = scene.cycles - sample_clamp = cscene.get("sample_clamp", False) - if ( - sample_clamp and - not cscene.is_property_set("sample_clamp_direct") and - not cscene.is_property_set("sample_clamp_indirect") - ): - - cscene.sample_clamp_direct = sample_clamp - cscene.sample_clamp_indirect = sample_clamp - - # Change of Volume Bounces in 271 - if bpy.data.version <= (2, 71, 0): - for scene in bpy.data.scenes: - cscene = scene.cycles - if not cscene.is_property_set("volume_bounces"): - cscene.volume_bounces = 1 + # Map of versions used by libraries. + library_versions = {} + library_versions[bpy.data.version] = [None] + for library in bpy.data.libraries: + library_versions.setdefault(library.version, []).append(library) - # Caustics Reflective/Refractive separation in 272 - if bpy.data.version <= (2, 72, 0): - for scene in bpy.data.scenes: - cscene = scene.cycles - if ( - cscene.get("no_caustics", False) and - not cscene.is_property_set("caustics_reflective") and - not cscene.is_property_set("caustics_refractive") - ): - cscene.caustics_reflective = False - cscene.caustics_refractive = False - - # Euler order was ZYX in previous versions. - if bpy.data.version <= (2, 73, 4): - foreach_cycles_node(mapping_node_order_flip) - - if bpy.data.version <= (2, 76, 5): - foreach_cycles_node(vector_curve_node_remap) - - # Baking types changed - if bpy.data.version <= (2, 76, 6): - for scene in bpy.data.scenes: - custom_bake_remap(scene) + # Do versioning per library, since they might have different versions. + max_need_versioning = (2, 79, 6) + for version, libraries in library_versions.items(): + if version > max_need_versioning: + continue - # Several default changes for 2.77 - if bpy.data.version <= (2, 76, 8): + # Scenes for scene in bpy.data.scenes: - cscene = scene.cycles - - # Samples - if not cscene.is_property_set("samples"): - cscene.samples = 10 - - # Preview Samples - if not cscene.is_property_set("preview_samples"): - cscene.preview_samples = 10 - - # Filter - if not cscene.is_property_set("filter_type"): - cscene.pixel_filter_type = 'GAUSSIAN' - - # Tile Order - if not cscene.is_property_set("tile_order"): - cscene.tile_order = 'CENTER' - + if scene.library not in libraries: + continue + + # Clamp Direct/Indirect separation in 270 + if version <= (2, 70, 0): + cscene = scene.cycles + sample_clamp = cscene.get("sample_clamp", False) + if ( + sample_clamp and + not cscene.is_property_set("sample_clamp_direct") and + not cscene.is_property_set("sample_clamp_indirect") + ): + + cscene.sample_clamp_direct = sample_clamp + cscene.sample_clamp_indirect = sample_clamp + + # Change of Volume Bounces in 271 + if version <= (2, 71, 0): + cscene = scene.cycles + if not cscene.is_property_set("volume_bounces"): + cscene.volume_bounces = 1 + + # Caustics Reflective/Refractive separation in 272 + if version <= (2, 72, 0): + cscene = scene.cycles + if ( + cscene.get("no_caustics", False) and + not cscene.is_property_set("caustics_reflective") and + not cscene.is_property_set("caustics_refractive") + ): + cscene.caustics_reflective = False + cscene.caustics_refractive = False + + # Baking types changed + if version <= (2, 76, 6): + custom_bake_remap(scene) + + # Several default changes for 2.77 + if version <= (2, 76, 8): + cscene = scene.cycles + + # Samples + if not cscene.is_property_set("samples"): + cscene.samples = 10 + + # Preview Samples + if not cscene.is_property_set("preview_samples"): + cscene.preview_samples = 10 + + # Filter + if not cscene.is_property_set("filter_type"): + cscene.pixel_filter_type = 'GAUSSIAN' + + # Tile Order + if not cscene.is_property_set("tile_order"): + cscene.tile_order = 'CENTER' + + if version <= (2, 76, 10): + cscene = scene.cycles + if cscene.is_property_set("filter_type"): + if not cscene.is_property_set("pixel_filter_type"): + cscene.pixel_filter_type = cscene.filter_type + if cscene.filter_type == 'BLACKMAN_HARRIS': + cscene.filter_type = 'GAUSSIAN' + + if version <= (2, 78, 2): + cscene = scene.cycles + if not cscene.is_property_set("light_sampling_threshold"): + cscene.light_sampling_threshold = 0.0 + + if version <= (2, 79, 0): + cscene = scene.cycles + # Default changes + if not cscene.is_property_set("aa_samples"): + cscene.aa_samples = 4 + if not cscene.is_property_set("preview_aa_samples"): + cscene.preview_aa_samples = 4 + if not cscene.is_property_set("blur_glossy"): + cscene.blur_glossy = 0.0 + if not cscene.is_property_set("sample_clamp_indirect"): + cscene.sample_clamp_indirect = 0.0 + + # Lamps for lamp in bpy.data.lamps: - clamp = lamp.cycles - - # MIS - if not clamp.is_property_set("use_multiple_importance_sampling"): - clamp.use_multiple_importance_sampling = False + if lamp.library not in libraries: + continue - for mat in bpy.data.materials: - cmat = mat.cycles + if version <= (2, 76, 5): + clamp = lamp.cycles - # Volume Sampling - if not cmat.is_property_set("volume_sampling"): - cmat.volume_sampling = 'DISTANCE' + # MIS + if not clamp.is_property_set("use_multiple_importance_sampling"): + clamp.use_multiple_importance_sampling = False - if bpy.data.version <= (2, 76, 9): + # Worlds for world in bpy.data.worlds: - cworld = world.cycles - - # World MIS Samples - if not cworld.is_property_set("samples"): - cworld.samples = 4 - - # World MIS Resolution - if not cworld.is_property_set("sample_map_resolution"): - cworld.sample_map_resolution = 256 - - if bpy.data.version <= (2, 76, 10): - for scene in bpy.data.scenes: - cscene = scene.cycles - if cscene.is_property_set("filter_type"): - if not cscene.is_property_set("pixel_filter_type"): - cscene.pixel_filter_type = cscene.filter_type - if cscene.filter_type == 'BLACKMAN_HARRIS': - cscene.filter_type = 'GAUSSIAN' - - if bpy.data.version <= (2, 78, 2): - for scene in bpy.data.scenes: - cscene = scene.cycles - if not cscene.is_property_set("light_sampling_threshold"): - cscene.light_sampling_threshold = 0.0 - - if bpy.data.version <= (2, 79, 0): - for scene in bpy.data.scenes: - cscene = scene.cycles - # Default changes - if not cscene.is_property_set("aa_samples"): - cscene.aa_samples = 4 - if not cscene.is_property_set("preview_aa_samples"): - cscene.preview_aa_samples = 4 - if not cscene.is_property_set("blur_glossy"): - cscene.blur_glossy = 0.0 - if not cscene.is_property_set("sample_clamp_indirect"): - cscene.sample_clamp_indirect = 0.0 - - if bpy.data.version <= (2, 79, 1): - displacement_nodes_insert() - - if bpy.data.version <= (2, 79, 2): + if world.library not in libraries: + continue + + if version <= (2, 76, 9): + cworld = world.cycles + + # World MIS Samples + if not cworld.is_property_set("samples"): + cworld.samples = 4 + + # World MIS Resolution + if not cworld.is_property_set("sample_map_resolution"): + cworld.sample_map_resolution = 256 + + if version <= (2, 79, 4): + cworld = world.cycles + # World MIS + if not cworld.is_property_set("sampling_method"): + if cworld.get("sample_as_light", True): + cworld.sampling_method = 'MANUAL' + else: + cworld.sampling_method = 'NONE' + + # Materials for mat in bpy.data.materials: - cmat = mat.cycles - if not cmat.is_property_set("displacement_method"): - cmat.displacement_method = 'BUMP' - - foreach_cycles_node(displacement_principled_nodes) + if mat.library not in libraries: + continue + + if version <= (2, 76, 5): + cmat = mat.cycles + # Volume Sampling + if not cmat.is_property_set("volume_sampling"): + cmat.volume_sampling = 'DISTANCE' + + if version <= (2, 79, 2): + cmat = mat.cycles + if not cmat.is_property_set("displacement_method"): + cmat.displacement_method = 'BUMP' + + # Change default to bump again. + if version <= (2, 79, 6): + cmat = mat.cycles + if not cmat.is_property_set("displacement_method"): + cmat.displacement_method = 'DISPLACEMENT' + + # Nodes + for nodetree, library in foreach_cycles_nodetree(): + if library not in libraries: + continue + + # Euler order was ZYX in previous versions. + if version <= (2, 73, 4): + for node in nodetree.nodes: + mapping_node_order_flip(node) + + if version <= (2, 76, 5): + for node in nodetree.nodes: + vector_curve_node_remap(node) + + if version <= (2, 79, 1): + displacement_node_insert(nodetree) + + if version <= (2, 79, 2): + for node in nodetree.nodes: + displacement_principled_nodes(node) + + if version <= (2, 79, 3): + # Switch to squared roughness convention + square_roughness_node_insert(nodetree) + + if version <= (2, 79, 4): + ambient_occlusion_node_relink(nodetree) - if bpy.data.version <= (2, 79, 3): - # Switch to squared roughness convention - square_roughness_nodes_insert() - - if bpy.data.version <= (2, 79, 4): - for world in bpy.data.worlds: - cworld = world.cycles - # World MIS - if not cworld.is_property_set("sampling_method"): - if cworld.get("sample_as_light", True): - cworld.sampling_method = 'MANUAL' - else: - cworld.sampling_method = 'NONE' - - ambient_occlusion_nodes_relink() - - if bpy.data.version <= (2, 79, 6): - # Change default to bump again. - for mat in bpy.data.materials: - cmat = mat.cycles - if not cmat.is_property_set("displacement_method"): - cmat.displacement_method = 'DISPLACEMENT' diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 3a22782f31e..85b1ec4d958 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -762,6 +762,14 @@ static IDProperty *rna_IDPropertyWrapPtr_idprops(PointerRNA *ptr, bool UNUSED(cr return ptr->data; } +static void rna_Library_version_get(PointerRNA *ptr, int *value) +{ + Library *lib = (Library *)ptr->data; + value[0] = lib->versionfile / 100; + value[1] = lib->versionfile % 100; + value[2] = lib->subversionfile; +} + #else static void rna_def_ID_properties(BlenderRNA *brna) @@ -1110,6 +1118,12 @@ static void rna_def_library(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "packedfile"); RNA_def_property_ui_text(prop, "Packed File", ""); + prop = RNA_def_int_vector(srna, "version", 3, NULL, 0, INT_MAX, + "Version", "Version of Blender the library .blend was saved with", 0, INT_MAX); + RNA_def_property_int_funcs(prop, "rna_Library_version_get", NULL, NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_flag(prop, PROP_THICK_WRAP); + func = RNA_def_function(srna, "reload", "WM_lib_reload"); RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_CONTEXT); RNA_def_function_ui_description(func, "Reload this library and all its linked data-blocks"); -- cgit v1.2.3