/* * Copyright 2011, Blender Foundation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "device.h" #include "device_intern.h" #include "device_network.h" #include "util_foreach.h" #include "util_list.h" #include "util_map.h" #include "util_time.h" CCL_NAMESPACE_BEGIN class MultiDevice : public Device { public: struct SubDevice { SubDevice(Device *device_) : device(device_) {} Device *device; map ptr_map; }; list devices; device_ptr unique_ptr; MultiDevice(DeviceInfo& info, bool background_) : unique_ptr(1) { Device *device; background = background_; foreach(DeviceInfo& subinfo, info.multi_devices) { device = Device::create(subinfo, background); devices.push_back(SubDevice(device)); } #if 0 //def WITH_NETWORK /* try to add network devices */ ServerDiscovery discovery(true); time_sleep(1.0); list servers = discovery.get_server_list(); foreach(string& server, servers) { device = device_network_create(info, server.c_str()); if(device) devices.push_back(SubDevice(device)); } #endif } ~MultiDevice() { foreach(SubDevice& sub, devices) delete sub.device; } bool support_full_kernel() { foreach(SubDevice& sub, devices) { if(!sub.device->support_full_kernel()) return false; } return true; } const string& error_message() { foreach(SubDevice& sub, devices) { if(sub.device->error_message() != "") { if(error_msg == "") error_msg = sub.device->error_message(); break; } } return error_msg; } string description() { /* create map to find duplicate descriptions */ map dupli_map; map::iterator dt; foreach(SubDevice& sub, devices) { string key = sub.device->description(); if(dupli_map.find(key) == dupli_map.end()) dupli_map[key] = 1; else dupli_map[key]++; } /* generate string */ stringstream desc; bool first = true; for(dt = dupli_map.begin(); dt != dupli_map.end(); dt++) { if(!first) desc << ", "; first = false; if(dt->second > 1) desc << dt->second << "x " << dt->first; else desc << dt->first; } return desc.str(); } bool load_kernels(bool experimental) { foreach(SubDevice& sub, devices) if(!sub.device->load_kernels(experimental)) return false; return true; } void mem_alloc(device_memory& mem, MemoryType type) { foreach(SubDevice& sub, devices) { mem.device_pointer = 0; sub.device->mem_alloc(mem, type); sub.ptr_map[unique_ptr] = mem.device_pointer; } mem.device_pointer = unique_ptr++; } void mem_copy_to(device_memory& mem) { device_ptr tmp = mem.device_pointer; foreach(SubDevice& sub, devices) { mem.device_pointer = sub.ptr_map[tmp]; sub.device->mem_copy_to(mem); } mem.device_pointer = tmp; } void mem_copy_from(device_memory& mem, int y, int w, int h, int elem) { device_ptr tmp = mem.device_pointer; int i = 0, sub_h = h/devices.size(); foreach(SubDevice& sub, devices) { int sy = y + i*sub_h; int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h; mem.device_pointer = sub.ptr_map[tmp]; sub.device->mem_copy_from(mem, sy, w, sh, elem); i++; } mem.device_pointer = tmp; } void mem_zero(device_memory& mem) { device_ptr tmp = mem.device_pointer; foreach(SubDevice& sub, devices) { mem.device_pointer = sub.ptr_map[tmp]; sub.device->mem_zero(mem); } mem.device_pointer = tmp; } void mem_free(device_memory& mem) { device_ptr tmp = mem.device_pointer; foreach(SubDevice& sub, devices) { mem.device_pointer = sub.ptr_map[tmp]; sub.device->mem_free(mem); sub.ptr_map.erase(sub.ptr_map.find(tmp)); } mem.device_pointer = 0; } void const_copy_to(const char *name, void *host, size_t size) { foreach(SubDevice& sub, devices) sub.device->const_copy_to(name, host, size); } void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic) { foreach(SubDevice& sub, devices) { mem.device_pointer = 0; sub.device->tex_alloc(name, mem, interpolation, periodic); sub.ptr_map[unique_ptr] = mem.device_pointer; } mem.device_pointer = unique_ptr++; } void tex_free(device_memory& mem) { device_ptr tmp = mem.device_pointer; foreach(SubDevice& sub, devices) { mem.device_pointer = sub.ptr_map[tmp]; sub.device->tex_free(mem); sub.ptr_map.erase(sub.ptr_map.find(tmp)); } mem.device_pointer = 0; } void pixels_alloc(device_memory& mem) { foreach(SubDevice& sub, devices) { mem.device_pointer = 0; sub.device->pixels_alloc(mem); sub.ptr_map[unique_ptr] = mem.device_pointer; } mem.device_pointer = unique_ptr++; } void pixels_free(device_memory& mem) { device_ptr tmp = mem.device_pointer; foreach(SubDevice& sub, devices) { mem.device_pointer = sub.ptr_map[tmp]; sub.device->pixels_free(mem); sub.ptr_map.erase(sub.ptr_map.find(tmp)); } mem.device_pointer = 0; } void pixels_copy_from(device_memory& mem, int y, int w, int h) { device_ptr tmp = mem.device_pointer; int i = 0, sub_h = h/devices.size(); foreach(SubDevice& sub, devices) { int sy = y + i*sub_h; int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h; mem.device_pointer = sub.ptr_map[tmp]; sub.device->pixels_copy_from(mem, sy, w, sh); i++; } mem.device_pointer = tmp; } void draw_pixels(device_memory& rgba, int y, int w, int h, int dy, int width, int height, bool transparent) { device_ptr tmp = rgba.device_pointer; int i = 0, sub_h = h/devices.size(); int sub_height = height/devices.size(); foreach(SubDevice& sub, devices) { int sy = y + i*sub_h; int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h; int sheight = (i == (int)devices.size() - 1)? height - sub_height*i: sub_height; int sdy = dy + i*sub_height; /* adjust math for w/width */ rgba.device_pointer = sub.ptr_map[tmp]; sub.device->draw_pixels(rgba, sy, w, sh, sdy, width, sheight, transparent); i++; } rgba.device_pointer = tmp; } void task_add(DeviceTask& task) { ThreadQueue tasks; task.split(tasks, devices.size()); foreach(SubDevice& sub, devices) { DeviceTask subtask; if(tasks.worker_wait_pop(subtask)) { if(task.buffer) subtask.buffer = sub.ptr_map[task.buffer]; if(task.rng_state) subtask.rng_state = sub.ptr_map[task.rng_state]; if(task.rgba) subtask.rgba = sub.ptr_map[task.rgba]; if(task.shader_input) subtask.shader_input = sub.ptr_map[task.shader_input]; if(task.shader_output) subtask.shader_output = sub.ptr_map[task.shader_output]; sub.device->task_add(subtask); } } } void task_wait() { foreach(SubDevice& sub, devices) sub.device->task_wait(); } void task_cancel() { foreach(SubDevice& sub, devices) sub.device->task_cancel(); } }; Device *device_multi_create(DeviceInfo& info, bool background) { return new MultiDevice(info, background); } static void device_multi_add(vector& devices, DeviceType type, bool with_display, const char *id_fmt, int num) { DeviceInfo info; /* create map to find duplicate descriptions */ map dupli_map; map::iterator dt; int num_added = 0, num_display = 0; foreach(DeviceInfo& subinfo, devices) { if(subinfo.type == type) { if(subinfo.display_device) { if(with_display) num_display++; else continue; } string key = subinfo.description; if(dupli_map.find(key) == dupli_map.end()) dupli_map[key] = 1; else dupli_map[key]++; info.multi_devices.push_back(subinfo); if(subinfo.display_device) info.display_device = true; num_added++; } } if(num_added <= 1 || (with_display && num_display == 0)) return; /* generate string */ stringstream desc; vector last_tokens; bool first = true; for(dt = dupli_map.begin(); dt != dupli_map.end(); dt++) { if(!first) desc << " + "; first = false; /* get name and count */ string name = dt->first; int count = dt->second; /* strip common prefixes */ vector tokens; string_split(tokens, dt->first); if(tokens.size() > 1) { int i; for(i = 0; i < tokens.size() && i < last_tokens.size(); i++) if(tokens[i] != last_tokens[i]) break; name = ""; for(; i < tokens.size(); i++) { name += tokens[i]; if(i != tokens.size() - 1) name += " "; } } last_tokens = tokens; /* add */ if(count > 1) desc << name << " (" << count << "x)"; else desc << name; } /* add info */ info.type = DEVICE_MULTI; info.description = desc.str(); info.id = string_printf(id_fmt, num); info.display_device = with_display; info.num = 0; if(with_display) devices.push_back(info); else devices.insert(devices.begin(), info); } void device_multi_info(vector& devices) { int num = 0; device_multi_add(devices, DEVICE_CUDA, false, "CUDA_MULTI_%d", num++); device_multi_add(devices, DEVICE_CUDA, true, "CUDA_MULTI_%d", num++); num = 0; device_multi_add(devices, DEVICE_OPENCL, false, "OPENCL_MULTI_%d", num++); device_multi_add(devices, DEVICE_OPENCL, true, "OPENCL_MULTI_%d", num++); } CCL_NAMESPACE_END