Changeset View
Changeset View
Standalone View
Standalone View
intern/cycles/device/opencl/opencl_util.cpp
| Show First 20 Lines • Show All 237 Lines • ▼ Show 20 Lines | string OpenCLCache::get_kernel_md5() | ||||
| thread_scoped_lock lock(self.kernel_md5_lock); | thread_scoped_lock lock(self.kernel_md5_lock); | ||||
| if(self.kernel_md5.empty()) { | if(self.kernel_md5.empty()) { | ||||
| self.kernel_md5 = path_files_md5_hash(path_get("source")); | self.kernel_md5 = path_files_md5_hash(path_get("source")); | ||||
| } | } | ||||
| return self.kernel_md5; | return self.kernel_md5; | ||||
| } | } | ||||
| OpenCLDeviceBase::OpenCLProgram::OpenCLProgram(OpenCLDeviceBase *device, | OpenCLDevice::OpenCLProgram::OpenCLProgram(OpenCLDevice *device, | ||||
| const string& program_name, | const string& program_name, | ||||
| const string& kernel_file, | const string& kernel_file, | ||||
| const string& kernel_build_options, | const string& kernel_build_options, | ||||
| bool use_stdout) | bool use_stdout) | ||||
| : device(device), | : device(device), | ||||
| program_name(program_name), | program_name(program_name), | ||||
| kernel_file(kernel_file), | kernel_file(kernel_file), | ||||
| kernel_build_options(kernel_build_options), | kernel_build_options(kernel_build_options), | ||||
| use_stdout(use_stdout) | use_stdout(use_stdout) | ||||
| { | { | ||||
| loaded = false; | loaded = false; | ||||
| program = NULL; | program = NULL; | ||||
| } | } | ||||
| OpenCLDeviceBase::OpenCLProgram::~OpenCLProgram() | OpenCLDevice::OpenCLProgram::~OpenCLProgram() | ||||
| { | { | ||||
| release(); | release(); | ||||
| } | } | ||||
| void OpenCLDeviceBase::OpenCLProgram::release() | void OpenCLDevice::OpenCLProgram::release() | ||||
| { | { | ||||
| for(map<ustring, cl_kernel>::iterator kernel = kernels.begin(); kernel != kernels.end(); ++kernel) { | for(map<ustring, cl_kernel>::iterator kernel = kernels.begin(); kernel != kernels.end(); ++kernel) { | ||||
| if(kernel->second) { | if(kernel->second) { | ||||
| clReleaseKernel(kernel->second); | clReleaseKernel(kernel->second); | ||||
| kernel->second = NULL; | kernel->second = NULL; | ||||
| } | } | ||||
| } | } | ||||
| if(program) { | if(program) { | ||||
| clReleaseProgram(program); | clReleaseProgram(program); | ||||
| program = NULL; | program = NULL; | ||||
| } | } | ||||
| } | } | ||||
| void OpenCLDeviceBase::OpenCLProgram::add_log(const string& msg, bool debug) | void OpenCLDevice::OpenCLProgram::add_log(const string& msg, bool debug) | ||||
| { | { | ||||
| if(!use_stdout) { | if(!use_stdout) { | ||||
| log += msg + "\n"; | log += msg + "\n"; | ||||
| } | } | ||||
| else if(!debug) { | else if(!debug) { | ||||
| printf("%s\n", msg.c_str()); | printf("%s\n", msg.c_str()); | ||||
| fflush(stdout); | fflush(stdout); | ||||
| } | } | ||||
| else { | else { | ||||
| VLOG(2) << msg; | VLOG(2) << msg; | ||||
| } | } | ||||
| } | } | ||||
| void OpenCLDeviceBase::OpenCLProgram::add_error(const string& msg) | void OpenCLDevice::OpenCLProgram::add_error(const string& msg) | ||||
| { | { | ||||
| if(use_stdout) { | if(use_stdout) { | ||||
| fprintf(stderr, "%s\n", msg.c_str()); | fprintf(stderr, "%s\n", msg.c_str()); | ||||
| } | } | ||||
| if(error_msg == "") { | if(error_msg == "") { | ||||
| error_msg += "\n"; | error_msg += "\n"; | ||||
| } | } | ||||
| error_msg += msg; | error_msg += msg; | ||||
| } | } | ||||
| void OpenCLDeviceBase::OpenCLProgram::add_kernel(ustring name) | void OpenCLDevice::OpenCLProgram::add_kernel(ustring name) | ||||
| { | { | ||||
| if(!kernels.count(name)) { | if(!kernels.count(name)) { | ||||
| kernels[name] = NULL; | kernels[name] = NULL; | ||||
| } | } | ||||
| } | } | ||||
| bool OpenCLDeviceBase::OpenCLProgram::build_kernel(const string *debug_src) | bool OpenCLDevice::OpenCLProgram::build_kernel(const string *debug_src) | ||||
| { | { | ||||
| string build_options; | string build_options; | ||||
| build_options = device->kernel_build_options(debug_src) + kernel_build_options; | build_options = device->kernel_build_options(debug_src) + kernel_build_options; | ||||
| VLOG(1) << "Build options passed to clBuildProgram: '" | VLOG(1) << "Build options passed to clBuildProgram: '" | ||||
| << build_options << "'."; | << build_options << "'."; | ||||
| cl_int ciErr = clBuildProgram(program, 0, NULL, build_options.c_str(), NULL, NULL); | cl_int ciErr = clBuildProgram(program, 0, NULL, build_options.c_str(), NULL, NULL); | ||||
| Show All 15 Lines | if(ret_val_size > 1) { | ||||
| if(!(ret_val_size == 2 && build_log[0] == '\n')) { | if(!(ret_val_size == 2 && build_log[0] == '\n')) { | ||||
| add_log(string("OpenCL program ") + program_name + " build output: " + string(&build_log[0]), ciErr == CL_SUCCESS); | add_log(string("OpenCL program ") + program_name + " build output: " + string(&build_log[0]), ciErr == CL_SUCCESS); | ||||
| } | } | ||||
| } | } | ||||
| return (ciErr == CL_SUCCESS); | return (ciErr == CL_SUCCESS); | ||||
| } | } | ||||
| bool OpenCLDeviceBase::OpenCLProgram::compile_kernel(const string *debug_src) | bool OpenCLDevice::OpenCLProgram::compile_kernel(const string *debug_src) | ||||
| { | { | ||||
| string source = "#include \"kernel/kernels/opencl/" + kernel_file + "\"\n"; | string source = "#include \"kernel/kernels/opencl/" + kernel_file + "\"\n"; | ||||
| /* We compile kernels consisting of many files. unfortunately OpenCL | /* We compile kernels consisting of many files. unfortunately OpenCL | ||||
| * kernel caches do not seem to recognize changes in included files. | * kernel caches do not seem to recognize changes in included files. | ||||
| * so we force recompile on changes by adding the md5 hash of all files. | * so we force recompile on changes by adding the md5 hash of all files. | ||||
| */ | */ | ||||
| source = path_source_replace_includes(source, path_get("source")); | source = path_source_replace_includes(source, path_get("source")); | ||||
| source += "\n// " + util_md5_string(source) + "\n"; | source += "\n// " + util_md5_string(source) + "\n"; | ||||
| Show All 31 Lines | |||||
| } | } | ||||
| static void escape_python_string(string& str) | static void escape_python_string(string& str) | ||||
| { | { | ||||
| /* Escape string to be passed as a Python raw string with '' quotes'. */ | /* Escape string to be passed as a Python raw string with '' quotes'. */ | ||||
| string_replace(str, "'", "\'"); | string_replace(str, "'", "\'"); | ||||
| } | } | ||||
| bool OpenCLDeviceBase::OpenCLProgram::compile_separate(const string& clbin) | bool OpenCLDevice::OpenCLProgram::compile_separate(const string& clbin) | ||||
| { | { | ||||
| vector<string> args; | vector<string> args; | ||||
| args.push_back("--background"); | args.push_back("--background"); | ||||
| args.push_back("--factory-startup"); | args.push_back("--factory-startup"); | ||||
| args.push_back("--python-expr"); | args.push_back("--python-expr"); | ||||
| const char *force_all_platforms = (DebugFlags().opencl.kernel_type != DebugFlags::OpenCL::KERNEL_DEFAULT)? "true" : "false"; | |||||
| int device_platform_id = device->device_num; | int device_platform_id = device->device_num; | ||||
| string device_name = device->device_name; | string device_name = device->device_name; | ||||
| string platform_name = device->platform_name; | string platform_name = device->platform_name; | ||||
| string build_options = device->kernel_build_options(NULL) + kernel_build_options; | string build_options = device->kernel_build_options(NULL) + kernel_build_options; | ||||
| string kernel_file_escaped = kernel_file; | string kernel_file_escaped = kernel_file; | ||||
| string clbin_escaped = clbin; | string clbin_escaped = clbin; | ||||
| escape_python_string(device_name); | escape_python_string(device_name); | ||||
| escape_python_string(platform_name); | escape_python_string(platform_name); | ||||
| escape_python_string(build_options); | escape_python_string(build_options); | ||||
| escape_python_string(kernel_file_escaped); | escape_python_string(kernel_file_escaped); | ||||
| escape_python_string(clbin_escaped); | escape_python_string(clbin_escaped); | ||||
| args.push_back( | args.push_back( | ||||
| string_printf( | string_printf( | ||||
| "import _cycles; _cycles.opencl_compile(r'%s', r'%d', r'%s', r'%s', r'%s', r'%s', r'%s')", | "import _cycles; _cycles.opencl_compile(r'%d', r'%s', r'%s', r'%s', r'%s', r'%s')", | ||||
| force_all_platforms, | |||||
| device_platform_id, | device_platform_id, | ||||
| device_name.c_str(), | device_name.c_str(), | ||||
| platform_name.c_str(), | platform_name.c_str(), | ||||
| build_options.c_str(), | build_options.c_str(), | ||||
| kernel_file_escaped.c_str(), | kernel_file_escaped.c_str(), | ||||
| clbin_escaped.c_str())); | clbin_escaped.c_str())); | ||||
| double starttime = time_dt(); | double starttime = time_dt(); | ||||
| add_log(string("Cycles: compiling OpenCL program ") + program_name + "...", false); | add_log(string("Cycles: compiling OpenCL program ") + program_name + "...", false); | ||||
| add_log(string("Build flags: ") + kernel_build_options, true); | add_log(string("Build flags: ") + kernel_build_options, true); | ||||
| if(!system_call_self(args) || !path_exists(clbin)) { | if(!system_call_self(args) || !path_exists(clbin)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| double elapsed = time_dt() - starttime; | double elapsed = time_dt() - starttime; | ||||
| add_log(string_printf("Kernel compilation of %s finished in %.2lfs.", program_name.c_str(), elapsed), false); | add_log(string_printf("Kernel compilation of %s finished in %.2lfs.", program_name.c_str(), elapsed), false); | ||||
| return load_binary(clbin); | return load_binary(clbin); | ||||
| } | } | ||||
| /* Compile opencl kernel. This method is called from the _cycles Python | /* Compile opencl kernel. This method is called from the _cycles Python | ||||
| * module compile kernels. Parameters must match function above. */ | * module compile kernels. Parameters must match function above. */ | ||||
| bool device_opencl_compile_kernel(const vector<string>& parameters) | bool device_opencl_compile_kernel(const vector<string>& parameters) | ||||
| { | { | ||||
| bool force_all_platforms = parameters[0] == "true"; | int device_platform_id = std::stoi(parameters[0]); | ||||
| int device_platform_id = std::stoi(parameters[1]); | const string& device_name = parameters[1]; | ||||
| const string& device_name = parameters[2]; | const string& platform_name = parameters[2]; | ||||
| const string& platform_name = parameters[3]; | const string& build_options = parameters[3]; | ||||
| const string& build_options = parameters[4]; | const string& kernel_file = parameters[4]; | ||||
| const string& kernel_file = parameters[5]; | const string& binary_path = parameters[5]; | ||||
| const string& binary_path = parameters[6]; | |||||
| if(clewInit() != CLEW_SUCCESS) { | if(clewInit() != CLEW_SUCCESS) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| vector<OpenCLPlatformDevice> usable_devices; | vector<OpenCLPlatformDevice> usable_devices; | ||||
| OpenCLInfo::get_usable_devices(&usable_devices, force_all_platforms); | OpenCLInfo::get_usable_devices(&usable_devices); | ||||
| if(device_platform_id >= usable_devices.size()) { | if(device_platform_id >= usable_devices.size()) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| OpenCLPlatformDevice& platform_device = usable_devices[device_platform_id]; | OpenCLPlatformDevice& platform_device = usable_devices[device_platform_id]; | ||||
| if(platform_device.platform_name != platform_name || | if(platform_device.platform_name != platform_name || | ||||
| platform_device.device_name != device_name) | platform_device.device_name != device_name) | ||||
| { | { | ||||
| Show All 36 Lines | if(err == CL_SUCCESS) { | ||||
| clReleaseProgram(program); | clReleaseProgram(program); | ||||
| } | } | ||||
| clReleaseContext(context); | clReleaseContext(context); | ||||
| return result; | return result; | ||||
| } | } | ||||
| bool OpenCLDeviceBase::OpenCLProgram::load_binary(const string& clbin, | bool OpenCLDevice::OpenCLProgram::load_binary(const string& clbin, | ||||
| const string *debug_src) | const string *debug_src) | ||||
| { | { | ||||
| /* read binary into memory */ | /* read binary into memory */ | ||||
| vector<uint8_t> binary; | vector<uint8_t> binary; | ||||
| if(!path_read_binary(clbin, binary)) { | if(!path_read_binary(clbin, binary)) { | ||||
| add_error(string_printf("OpenCL failed to read cached binary %s.", clbin.c_str())); | add_error(string_printf("OpenCL failed to read cached binary %s.", clbin.c_str())); | ||||
| return false; | return false; | ||||
| Show All 14 Lines | bool OpenCLDevice::OpenCLProgram::load_binary(const string& clbin, | ||||
| } | } | ||||
| if(!build_kernel(debug_src)) | if(!build_kernel(debug_src)) | ||||
| return false; | return false; | ||||
| return true; | return true; | ||||
| } | } | ||||
| bool OpenCLDeviceBase::OpenCLProgram::save_binary(const string& clbin) | bool OpenCLDevice::OpenCLProgram::save_binary(const string& clbin) | ||||
| { | { | ||||
| size_t size = 0; | size_t size = 0; | ||||
| clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL); | clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL); | ||||
| if(!size) | if(!size) | ||||
| return false; | return false; | ||||
| vector<uint8_t> binary(size); | vector<uint8_t> binary(size); | ||||
| uint8_t *bytes = &binary[0]; | uint8_t *bytes = &binary[0]; | ||||
| clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(uint8_t*), &bytes, NULL); | clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(uint8_t*), &bytes, NULL); | ||||
| return path_write_binary(clbin, binary); | return path_write_binary(clbin, binary); | ||||
| } | } | ||||
| void OpenCLDeviceBase::OpenCLProgram::load() | void OpenCLDevice::OpenCLProgram::load() | ||||
| { | { | ||||
| assert(device); | assert(device); | ||||
| loaded = false; | loaded = false; | ||||
| string device_md5 = device->device_md5_hash(kernel_build_options); | string device_md5 = device->device_md5_hash(kernel_build_options); | ||||
| /* Try to use cached kernel. */ | /* Try to use cached kernel. */ | ||||
| ▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | if(device->opencl_error(ciErr)) { | ||||
| add_error(string("Error getting kernel ") + name + " from program " + program_name + ": " + clewErrorString(ciErr)); | add_error(string("Error getting kernel ") + name + " from program " + program_name + ": " + clewErrorString(ciErr)); | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| loaded = true; | loaded = true; | ||||
| } | } | ||||
| void OpenCLDeviceBase::OpenCLProgram::report_error() | void OpenCLDevice::OpenCLProgram::report_error() | ||||
| { | { | ||||
| /* If loaded is true, there was no error. */ | /* If loaded is true, there was no error. */ | ||||
| if(loaded) return; | if(loaded) return; | ||||
| /* if use_stdout is true, the error was already reported. */ | /* if use_stdout is true, the error was already reported. */ | ||||
| if(use_stdout) return; | if(use_stdout) return; | ||||
| cerr << error_msg << endl; | cerr << error_msg << endl; | ||||
| if(!compile_output.empty()) { | if(!compile_output.empty()) { | ||||
| cerr << "OpenCL kernel build output for " << program_name << ":" << endl; | cerr << "OpenCL kernel build output for " << program_name << ":" << endl; | ||||
| cerr << compile_output << endl; | cerr << compile_output << endl; | ||||
| } | } | ||||
| } | } | ||||
| cl_kernel OpenCLDeviceBase::OpenCLProgram::operator()() | cl_kernel OpenCLDevice::OpenCLProgram::operator()() | ||||
| { | { | ||||
| assert(kernels.size() == 1); | assert(kernels.size() == 1); | ||||
| return kernels.begin()->second; | return kernels.begin()->second; | ||||
| } | } | ||||
| cl_kernel OpenCLDeviceBase::OpenCLProgram::operator()(ustring name) | cl_kernel OpenCLDevice::OpenCLProgram::operator()(ustring name) | ||||
| { | { | ||||
| assert(kernels.count(name)); | assert(kernels.count(name)); | ||||
| return kernels[name]; | return kernels[name]; | ||||
| } | } | ||||
| cl_device_type OpenCLInfo::device_type() | cl_device_type OpenCLInfo::device_type() | ||||
| { | { | ||||
| switch(DebugFlags().opencl.device_type) | switch(DebugFlags().opencl.device_type) | ||||
| Show All 37 Lines | bool OpenCLInfo::kernel_use_advanced_shading(const string& platform) | ||||
| else if(platform == "Intel(R) OpenCL") | else if(platform == "Intel(R) OpenCL") | ||||
| return true; | return true; | ||||
| /* Make sure officially unsupported OpenCL platforms | /* Make sure officially unsupported OpenCL platforms | ||||
| * does not set up to use advanced shading. | * does not set up to use advanced shading. | ||||
| */ | */ | ||||
| return false; | return false; | ||||
| } | } | ||||
| bool OpenCLInfo::kernel_use_split(const string& platform_name, | |||||
| const cl_device_type device_type) | |||||
| { | |||||
| if(DebugFlags().opencl.kernel_type == DebugFlags::OpenCL::KERNEL_SPLIT) { | |||||
| VLOG(1) << "Forcing split kernel to use."; | |||||
| return true; | |||||
| } | |||||
| if(DebugFlags().opencl.kernel_type == DebugFlags::OpenCL::KERNEL_MEGA) { | |||||
| VLOG(1) << "Forcing mega kernel to use."; | |||||
| return false; | |||||
| } | |||||
| /* TODO(sergey): Replace string lookups with more enum-like API, | |||||
| * similar to device/vendor checks blender's gpu. | |||||
| */ | |||||
| if(platform_name == "AMD Accelerated Parallel Processing" && | |||||
| device_type == CL_DEVICE_TYPE_GPU) | |||||
| { | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| bool OpenCLInfo::device_supported(const string& platform_name, | bool OpenCLInfo::device_supported(const string& platform_name, | ||||
| const cl_device_id device_id) | const cl_device_id device_id) | ||||
| { | { | ||||
| cl_device_type device_type; | cl_device_type device_type; | ||||
| if(!get_device_type(device_id, &device_type)) { | if(!get_device_type(device_id, &device_type)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| string device_name; | string device_name; | ||||
| ▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | string OpenCLInfo::get_hardware_id(const string& platform_name, cl_device_id device_id) | ||||
| } | } | ||||
| /* No general way to get a hardware ID from OpenCL => give up. */ | /* No general way to get a hardware ID from OpenCL => give up. */ | ||||
| return ""; | return ""; | ||||
| } | } | ||||
| void OpenCLInfo::get_usable_devices(vector<OpenCLPlatformDevice> *usable_devices, | void OpenCLInfo::get_usable_devices(vector<OpenCLPlatformDevice> *usable_devices, | ||||
| bool force_all) | bool force_all) | ||||
| { | { | ||||
| const bool force_all_platforms = force_all || | |||||
| (DebugFlags().opencl.kernel_type != DebugFlags::OpenCL::KERNEL_DEFAULT); | |||||
| const cl_device_type device_type = OpenCLInfo::device_type(); | const cl_device_type device_type = OpenCLInfo::device_type(); | ||||
| static bool first_time = true; | static bool first_time = true; | ||||
| #define FIRST_VLOG(severity) if(first_time) VLOG(severity) | #define FIRST_VLOG(severity) if(first_time) VLOG(severity) | ||||
| usable_devices->clear(); | usable_devices->clear(); | ||||
| if(device_type == 0) { | if(device_type == 0) { | ||||
| FIRST_VLOG(2) << "OpenCL devices are forced to be disabled."; | FIRST_VLOG(2) << "OpenCL devices are forced to be disabled."; | ||||
| ▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | for(int num = 0; num < device_ids.size(); num++) { | ||||
| << ", ignoring."; | << ", ignoring."; | ||||
| continue; | continue; | ||||
| } | } | ||||
| if(!device_version_check(device_id)) { | if(!device_version_check(device_id)) { | ||||
| FIRST_VLOG(2) << "Ignoring device " << device_name | FIRST_VLOG(2) << "Ignoring device " << device_name | ||||
| << " due to old compiler version."; | << " due to old compiler version."; | ||||
| continue; | continue; | ||||
| } | } | ||||
| if(force_all_platforms || | if(force_all || | ||||
| device_supported(platform_name, device_id)) | device_supported(platform_name, device_id)) | ||||
| { | { | ||||
| cl_device_type device_type; | cl_device_type device_type; | ||||
| if(!get_device_type(device_id, &device_type, &error)) { | if(!get_device_type(device_id, &device_type, &error)) { | ||||
| FIRST_VLOG(2) << "Ignoring device " << device_name | FIRST_VLOG(2) << "Ignoring device " << device_name | ||||
| << ", failed to fetch device type:" | << ", failed to fetch device type:" | ||||
| << string(clewErrorString(error)); | << string(clewErrorString(error)); | ||||
| continue; | continue; | ||||
| ▲ Show 20 Lines • Show All 383 Lines • Show Last 20 Lines | |||||