Code refactor: move more memory allocation logic into device API.
[blender-staging.git] / intern / cycles / device / device_multi.cpp
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdlib.h>
18 #include <sstream>
19
20 #include "device/device.h"
21 #include "device/device_intern.h"
22 #include "device/device_network.h"
23
24 #include "render/buffers.h"
25
26 #include "util/util_foreach.h"
27 #include "util/util_list.h"
28 #include "util/util_logging.h"
29 #include "util/util_map.h"
30 #include "util/util_time.h"
31
32 CCL_NAMESPACE_BEGIN
33
34 class MultiDevice : public Device
35 {
36 public:
37         struct SubDevice {
38                 explicit SubDevice(Device *device_)
39                 : device(device_) {}
40
41                 Device *device;
42                 map<device_ptr, device_ptr> ptr_map;
43         };
44
45         list<SubDevice> devices;
46         device_ptr unique_key;
47
48         MultiDevice(DeviceInfo& info, Stats &stats, bool background_)
49         : Device(info, stats, background_), unique_key(1)
50         {
51                 Device *device;
52
53                 foreach(DeviceInfo& subinfo, info.multi_devices) {
54                         device = Device::create(subinfo, sub_stats_, background);
55                         devices.push_back(SubDevice(device));
56                 }
57
58 #ifdef WITH_NETWORK
59                 /* try to add network devices */
60                 ServerDiscovery discovery(true);
61                 time_sleep(1.0);
62
63                 vector<string> servers = discovery.get_server_list();
64
65                 foreach(string& server, servers) {
66                         device = device_network_create(info, stats, 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         virtual bool show_samples() const
93         {
94                 if(devices.size() > 1) {
95                         return false;
96                 }
97                 return devices.front().device->show_samples();
98         }
99
100         bool load_kernels(const DeviceRequestedFeatures& requested_features)
101         {
102                 foreach(SubDevice& sub, devices)
103                         if(!sub.device->load_kernels(requested_features))
104                                 return false;
105
106                 return true;
107         }
108
109         void mem_alloc(device_memory& mem)
110         {
111                 device_ptr key = unique_key++;
112
113                 foreach(SubDevice& sub, devices) {
114                         mem.device = sub.device;
115                         mem.device_pointer = 0;
116
117                         sub.device->mem_alloc(mem);
118                         sub.ptr_map[key] = mem.device_pointer;
119                 }
120
121                 mem.device = this;
122                 mem.device_pointer = key;
123         }
124
125         void mem_copy_to(device_memory& mem)
126         {
127                 device_ptr existing_key = mem.device_pointer;
128                 device_ptr key = (existing_key)? existing_key: unique_key++;
129
130                 foreach(SubDevice& sub, devices) {
131                         mem.device = sub.device;
132                         mem.device_pointer = (existing_key)? sub.ptr_map[existing_key]: 0;
133
134                         sub.device->mem_copy_to(mem);
135                         sub.ptr_map[key] = mem.device_pointer;
136                 }
137
138                 mem.device = this;
139                 mem.device_pointer = key;
140         }
141
142         void mem_copy_from(device_memory& mem, int y, int w, int h, int elem)
143         {
144                 device_ptr key = mem.device_pointer;
145                 int i = 0, sub_h = h/devices.size();
146
147                 foreach(SubDevice& sub, devices) {
148                         int sy = y + i*sub_h;
149                         int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
150
151                         mem.device = sub.device;
152                         mem.device_pointer = sub.ptr_map[key];
153
154                         sub.device->mem_copy_from(mem, sy, w, sh, elem);
155                         i++;
156                 }
157
158                 mem.device = this;
159                 mem.device_pointer = key;
160         }
161
162         void mem_zero(device_memory& mem)
163         {
164                 device_ptr existing_key = mem.device_pointer;
165                 device_ptr key = (existing_key)? existing_key: unique_key++;
166
167                 foreach(SubDevice& sub, devices) {
168                         mem.device = sub.device;
169                         mem.device_pointer = (existing_key)? sub.ptr_map[existing_key]: 0;
170
171                         sub.device->mem_zero(mem);
172                         sub.ptr_map[key] = mem.device_pointer;
173                 }
174
175                 mem.device = this;
176                 mem.device_pointer = key;
177         }
178
179         void mem_free(device_memory& mem)
180         {
181                 device_ptr key = mem.device_pointer;
182
183                 foreach(SubDevice& sub, devices) {
184                         mem.device = sub.device;
185                         mem.device_pointer = sub.ptr_map[key];
186
187                         sub.device->mem_free(mem);
188                         sub.ptr_map.erase(sub.ptr_map.find(key));
189                 }
190
191                 mem.device = this;
192                 mem.device_pointer = 0;
193         }
194
195         void const_copy_to(const char *name, void *host, size_t size)
196         {
197                 foreach(SubDevice& sub, devices)
198                         sub.device->const_copy_to(name, host, size);
199         }
200
201         void draw_pixels(device_memory& rgba, int y, int w, int h, int dx, int dy, int width, int height, bool transparent,
202                 const DeviceDrawParams &draw_params)
203         {
204                 device_ptr key = rgba.device_pointer;
205                 int i = 0, sub_h = h/devices.size();
206                 int sub_height = height/devices.size();
207
208                 foreach(SubDevice& sub, devices) {
209                         int sy = y + i*sub_h;
210                         int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
211                         int sheight = (i == (int)devices.size() - 1)? height - sub_height*i: sub_height;
212                         int sdy = dy + i*sub_height;
213                         /* adjust math for w/width */
214
215                         rgba.device_pointer = sub.ptr_map[key];
216                         sub.device->draw_pixels(rgba, sy, w, sh, dx, sdy, width, sheight, transparent, draw_params);
217                         i++;
218                 }
219
220                 rgba.device_pointer = key;
221         }
222
223         void map_tile(Device *sub_device, RenderTile& tile)
224         {
225                 foreach(SubDevice& sub, devices) {
226                         if(sub.device == sub_device) {
227                                 if(tile.buffer) tile.buffer = sub.ptr_map[tile.buffer];
228                         }
229                 }
230         }
231
232         int device_number(Device *sub_device)
233         {
234                 int i = 0;
235
236                 foreach(SubDevice& sub, devices) {
237                         if(sub.device == sub_device)
238                                 return i;
239                         i++;
240                 }
241
242                 return -1;
243         }
244
245         void map_neighbor_tiles(Device *sub_device, RenderTile *tiles)
246         {
247                 for(int i = 0; i < 9; i++) {
248                         if(!tiles[i].buffers) {
249                                 continue;
250                         }
251                         /* If the tile was rendered on another device, copy its memory to
252                          * to the current device now, for the duration of the denoising task.
253                          * Note that this temporarily modifies the RenderBuffers and calls
254                          * the device, so this function is not thread safe. */
255                         device_vector<float> &mem = tiles[i].buffers->buffer;
256                         if(mem.device != sub_device) {
257                                 tiles[i].buffers->copy_from_device();
258
259                                 Device *original_device = mem.device;
260                                 device_ptr original_ptr = mem.device_pointer;
261
262                                 mem.device = sub_device;
263                                 mem.device_pointer = 0;
264
265                                 sub_device->mem_alloc(mem);
266                                 sub_device->mem_copy_to(mem);
267                                 tiles[i].buffer = mem.device_pointer;
268
269                                 mem.device = original_device;
270                                 mem.device_pointer = original_ptr;
271                         }
272                 }
273         }
274
275         void unmap_neighbor_tiles(Device * sub_device, RenderTile * tiles)
276         {
277                 for(int i = 0; i < 9; i++) {
278                         if(!tiles[i].buffers) {
279                                 continue;
280                         }
281
282                         device_vector<float> &mem = tiles[i].buffers->buffer;
283                         if(mem.device != sub_device) {
284                                 Device *original_device = mem.device;
285                                 device_ptr original_ptr = mem.device_pointer;
286                                 size_t original_size = mem.device_size;
287
288                                 mem.device = sub_device;
289                                 mem.device_pointer = tiles[i].buffer;
290
291                                 /* Copy denoised tile to the host. */
292                                 if(i == 4) {
293                                         tiles[i].buffers->copy_from_device();
294                                 }
295
296                                 sub_device->mem_free(mem);
297
298                                 mem.device = original_device;
299                                 mem.device_pointer = original_ptr;
300                                 mem.device_size = original_size;
301
302                                 /* Copy denoised tile to the original device. */
303                                 if(i == 4) {
304                                         mem.copy_to_device();
305                                 }
306                         }
307                 }
308         }
309
310         int get_split_task_count(DeviceTask& task)
311         {
312                 int total_tasks = 0;
313                 list<DeviceTask> tasks;
314                 task.split(tasks, devices.size());
315                 foreach(SubDevice& sub, devices) {
316                         if(!tasks.empty()) {
317                                 DeviceTask subtask = tasks.front();
318                                 tasks.pop_front();
319
320                                 total_tasks += sub.device->get_split_task_count(subtask);
321                         }
322                 }
323                 return total_tasks;
324         }
325
326         void task_add(DeviceTask& task)
327         {
328                 list<DeviceTask> tasks;
329                 task.split(tasks, devices.size());
330
331                 foreach(SubDevice& sub, devices) {
332                         if(!tasks.empty()) {
333                                 DeviceTask subtask = tasks.front();
334                                 tasks.pop_front();
335
336                                 if(task.buffer) subtask.buffer = sub.ptr_map[task.buffer];
337                                 if(task.rgba_byte) subtask.rgba_byte = sub.ptr_map[task.rgba_byte];
338                                 if(task.rgba_half) subtask.rgba_half = sub.ptr_map[task.rgba_half];
339                                 if(task.shader_input) subtask.shader_input = sub.ptr_map[task.shader_input];
340                                 if(task.shader_output) subtask.shader_output = sub.ptr_map[task.shader_output];
341
342                                 sub.device->task_add(subtask);
343                         }
344                 }
345         }
346
347         void task_wait()
348         {
349                 foreach(SubDevice& sub, devices)
350                         sub.device->task_wait();
351         }
352
353         void task_cancel()
354         {
355                 foreach(SubDevice& sub, devices)
356                         sub.device->task_cancel();
357         }
358
359 protected:
360         Stats sub_stats_;
361 };
362
363 Device *device_multi_create(DeviceInfo& info, Stats &stats, bool background)
364 {
365         return new MultiDevice(info, stats, background);
366 }
367
368 CCL_NAMESPACE_END
369