83e69b98f5d41e9ca1886851552fc77d14921d88
[blender-staging.git] / intern / cycles / device / device_multi.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include <stdlib.h>
20 #include <sstream>
21
22 #include "device.h"
23 #include "device_intern.h"
24 #include "device_network.h"
25
26 #include "util_foreach.h"
27 #include "util_list.h"
28 #include "util_map.h"
29 #include "util_time.h"
30
31 CCL_NAMESPACE_BEGIN
32
33 class MultiDevice : public Device
34 {
35 public:
36         struct SubDevice {
37                 SubDevice(Device *device_)
38                 : device(device_) {}
39
40                 Device *device;
41                 map<device_ptr, device_ptr> ptr_map;
42         };
43
44         list<SubDevice> devices;
45         device_ptr unique_ptr;
46
47         MultiDevice(DeviceInfo& info, bool background_)
48         : unique_ptr(1)
49         {
50                 Device *device;
51                 background = background_;
52
53                 foreach(DeviceInfo& subinfo, info.multi_devices) {
54                         device = Device::create(subinfo, background);
55                         devices.push_back(SubDevice(device));
56                 }
57
58 #if 0 //def WITH_NETWORK
59                 /* try to add network devices */
60                 ServerDiscovery discovery(true);
61                 time_sleep(1.0);
62
63                 list<string> servers = discovery.get_server_list();
64
65                 foreach(string& server, servers) {
66                         device = device_network_create(info, server.c_str());
67                         if(device)
68                                 devices.push_back(SubDevice(device));
69                 }
70 #endif
71         }
72
73         ~MultiDevice()
74         {
75                 foreach(SubDevice& sub, devices)
76                         delete sub.device;
77         }
78
79         const string& error_message()
80         {
81                 foreach(SubDevice& sub, devices) {
82                         if(sub.device->error_message() != "") {
83                                 if(error_msg == "")
84                                         error_msg = sub.device->error_message();
85                                 break;
86                         }
87                 }
88
89                 return error_msg;
90         }
91
92         bool load_kernels(bool experimental)
93         {
94                 foreach(SubDevice& sub, devices)
95                         if(!sub.device->load_kernels(experimental))
96                                 return false;
97
98                 return true;
99         }
100
101         void mem_alloc(device_memory& mem, MemoryType type)
102         {
103                 foreach(SubDevice& sub, devices) {
104                         mem.device_pointer = 0;
105                         sub.device->mem_alloc(mem, type);
106                         sub.ptr_map[unique_ptr] = mem.device_pointer;
107                 }
108
109                 mem.device_pointer = unique_ptr++;
110         }
111
112         void mem_copy_to(device_memory& mem)
113         {
114                 device_ptr tmp = mem.device_pointer;
115
116                 foreach(SubDevice& sub, devices) {
117                         mem.device_pointer = sub.ptr_map[tmp];
118                         sub.device->mem_copy_to(mem);
119                 }
120
121                 mem.device_pointer = tmp;
122         }
123
124         void mem_copy_from(device_memory& mem, int y, int w, int h, int elem)
125         {
126                 device_ptr tmp = mem.device_pointer;
127                 int i = 0, sub_h = h/devices.size();
128
129                 foreach(SubDevice& sub, devices) {
130                         int sy = y + i*sub_h;
131                         int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
132
133                         mem.device_pointer = sub.ptr_map[tmp];
134                         sub.device->mem_copy_from(mem, sy, w, sh, elem);
135                         i++;
136                 }
137
138                 mem.device_pointer = tmp;
139         }
140
141         void mem_zero(device_memory& mem)
142         {
143                 device_ptr tmp = mem.device_pointer;
144
145                 foreach(SubDevice& sub, devices) {
146                         mem.device_pointer = sub.ptr_map[tmp];
147                         sub.device->mem_zero(mem);
148                 }
149
150                 mem.device_pointer = tmp;
151         }
152
153         void mem_free(device_memory& mem)
154         {
155                 device_ptr tmp = mem.device_pointer;
156
157                 foreach(SubDevice& sub, devices) {
158                         mem.device_pointer = sub.ptr_map[tmp];
159                         sub.device->mem_free(mem);
160                         sub.ptr_map.erase(sub.ptr_map.find(tmp));
161                 }
162
163                 mem.device_pointer = 0;
164         }
165
166         void const_copy_to(const char *name, void *host, size_t size)
167         {
168                 foreach(SubDevice& sub, devices)
169                         sub.device->const_copy_to(name, host, size);
170         }
171
172         void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
173         {
174                 foreach(SubDevice& sub, devices) {
175                         mem.device_pointer = 0;
176                         sub.device->tex_alloc(name, mem, interpolation, periodic);
177                         sub.ptr_map[unique_ptr] = mem.device_pointer;
178                 }
179
180                 mem.device_pointer = unique_ptr++;
181         }
182
183         void tex_free(device_memory& mem)
184         {
185                 device_ptr tmp = mem.device_pointer;
186
187                 foreach(SubDevice& sub, devices) {
188                         mem.device_pointer = sub.ptr_map[tmp];
189                         sub.device->tex_free(mem);
190                         sub.ptr_map.erase(sub.ptr_map.find(tmp));
191                 }
192
193                 mem.device_pointer = 0;
194         }
195
196         void pixels_alloc(device_memory& mem)
197         {
198                 foreach(SubDevice& sub, devices) {
199                         mem.device_pointer = 0;
200                         sub.device->pixels_alloc(mem);
201                         sub.ptr_map[unique_ptr] = mem.device_pointer;
202                 }
203
204                 mem.device_pointer = unique_ptr++;
205         }
206
207         void pixels_free(device_memory& mem)
208         {
209                 device_ptr tmp = mem.device_pointer;
210
211                 foreach(SubDevice& sub, devices) {
212                         mem.device_pointer = sub.ptr_map[tmp];
213                         sub.device->pixels_free(mem);
214                         sub.ptr_map.erase(sub.ptr_map.find(tmp));
215                 }
216
217                 mem.device_pointer = 0;
218         }
219
220         void pixels_copy_from(device_memory& mem, int y, int w, int h)
221         {
222                 device_ptr tmp = mem.device_pointer;
223                 int i = 0, sub_h = h/devices.size();
224
225                 foreach(SubDevice& sub, devices) {
226                         int sy = y + i*sub_h;
227                         int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
228
229                         mem.device_pointer = sub.ptr_map[tmp];
230                         sub.device->pixels_copy_from(mem, sy, w, sh);
231                         i++;
232                 }
233
234                 mem.device_pointer = tmp;
235         }
236
237         void draw_pixels(device_memory& rgba, int y, int w, int h, int dy, int width, int height, bool transparent)
238         {
239                 device_ptr tmp = rgba.device_pointer;
240                 int i = 0, sub_h = h/devices.size();
241                 int sub_height = height/devices.size();
242
243                 foreach(SubDevice& sub, devices) {
244                         int sy = y + i*sub_h;
245                         int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
246                         int sheight = (i == (int)devices.size() - 1)? height - sub_height*i: sub_height;
247                         int sdy = dy + i*sub_height;
248                         /* adjust math for w/width */
249
250                         rgba.device_pointer = sub.ptr_map[tmp];
251                         sub.device->draw_pixels(rgba, sy, w, sh, sdy, width, sheight, transparent);
252                         i++;
253                 }
254
255                 rgba.device_pointer = tmp;
256         }
257
258         void task_add(DeviceTask& task)
259         {
260                 list<DeviceTask> tasks;
261                 task.split(tasks, devices.size());
262
263                 foreach(SubDevice& sub, devices) {
264                         if(!tasks.empty()) {
265                                 DeviceTask subtask = tasks.front();
266                                 tasks.pop_front();
267
268                                 if(task.buffer) subtask.buffer = sub.ptr_map[task.buffer];
269                                 if(task.rng_state) subtask.rng_state = sub.ptr_map[task.rng_state];
270                                 if(task.rgba) subtask.rgba = sub.ptr_map[task.rgba];
271                                 if(task.shader_input) subtask.shader_input = sub.ptr_map[task.shader_input];
272                                 if(task.shader_output) subtask.shader_output = sub.ptr_map[task.shader_output];
273
274                                 sub.device->task_add(subtask);
275                         }
276                 }
277         }
278
279         void task_wait()
280         {
281                 foreach(SubDevice& sub, devices)
282                         sub.device->task_wait();
283         }
284
285         void task_cancel()
286         {
287                 foreach(SubDevice& sub, devices)
288                         sub.device->task_cancel();
289         }
290 };
291
292 Device *device_multi_create(DeviceInfo& info, bool background)
293 {
294         return new MultiDevice(info, background);
295 }
296
297 static bool device_multi_add(vector<DeviceInfo>& devices, DeviceType type, bool with_display, bool with_advanced_shading, const char *id_fmt, int num)
298 {
299         DeviceInfo info;
300
301         /* create map to find duplicate descriptions */
302         map<string, int> dupli_map;
303         map<string, int>::iterator dt;
304         int num_added = 0, num_display = 0;
305
306         info.advanced_shading = with_advanced_shading;
307         info.pack_images = false;
308
309         foreach(DeviceInfo& subinfo, devices) {
310                 if(subinfo.type == type) {
311                         if(subinfo.advanced_shading != info.advanced_shading)
312                                 continue;
313                         if(subinfo.display_device) {
314                                 if(with_display)
315                                         num_display++;
316                                 else
317                                         continue;
318                         }
319
320                         string key = subinfo.description;
321
322                         if(dupli_map.find(key) == dupli_map.end())
323                                 dupli_map[key] = 1;
324                         else
325                                 dupli_map[key]++;
326
327                         info.multi_devices.push_back(subinfo);
328                         if(subinfo.display_device)
329                                 info.display_device = true;
330                         info.pack_images = info.pack_images || subinfo.pack_images;
331                         num_added++;
332                 }
333         }
334
335         if(num_added <= 1 || (with_display && num_display == 0))
336                 return false;
337
338         /* generate string */
339         stringstream desc;
340         vector<string> last_tokens;
341         bool first = true;
342
343         for(dt = dupli_map.begin(); dt != dupli_map.end(); dt++) {
344                 if(!first) desc << " + ";
345                 first = false;
346
347                 /* get name and count */
348                 string name = dt->first;
349                 int count = dt->second;
350
351                 /* strip common prefixes */
352                 vector<string> tokens;
353                 string_split(tokens, dt->first);
354
355                 if(tokens.size() > 1) {
356                         int i;
357
358                         for(i = 0; i < tokens.size() && i < last_tokens.size(); i++)
359                                 if(tokens[i] != last_tokens[i])
360                                         break;
361
362                         name = "";
363                         for(; i < tokens.size(); i++) {
364                                 name += tokens[i];
365                                 if(i != tokens.size() - 1)
366                                         name += " ";
367                         }
368                 }
369
370                 last_tokens = tokens;
371
372                 /* add */
373                 if(count > 1)
374                         desc << name << " (" << count << "x)";
375                 else
376                         desc << name;
377         }
378
379         /* add info */
380         info.type = DEVICE_MULTI;
381         info.description = desc.str();
382         info.id = string_printf(id_fmt, num);
383         info.display_device = with_display;
384         info.num = 0;
385
386         if(with_display)
387                 devices.push_back(info);
388         else
389                 devices.insert(devices.begin(), info);
390         
391         return true;
392 }
393
394 void device_multi_info(vector<DeviceInfo>& devices)
395 {
396         int num = 0;
397
398         if(!device_multi_add(devices, DEVICE_CUDA, false, true, "CUDA_MULTI_%d", num++))
399                 device_multi_add(devices, DEVICE_CUDA, false, false, "CUDA_MULTI_%d", num++);
400         if(!device_multi_add(devices, DEVICE_CUDA, true, true, "CUDA_MULTI_%d", num++))
401                 device_multi_add(devices, DEVICE_CUDA, true, false, "CUDA_MULTI_%d", num++);
402
403         num = 0;
404         if(!device_multi_add(devices, DEVICE_OPENCL, false, true, "OPENCL_MULTI_%d", num++))
405                 device_multi_add(devices, DEVICE_OPENCL, false, false, "OPENCL_MULTI_%d", num++);
406         if(!device_multi_add(devices, DEVICE_OPENCL, true, true, "OPENCL_MULTI_%d", num++))
407                 device_multi_add(devices, DEVICE_OPENCL, true, false, "OPENCL_MULTI_%d", num++);
408 }
409
410 CCL_NAMESPACE_END
411