Fix error extracting date in manpage generator
[blender.git] / intern / cycles / device / device.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 <string.h>
19
20 #include "bvh/bvh2.h"
21
22 #include "device/device.h"
23 #include "device/device_intern.h"
24
25 #include "util/util_foreach.h"
26 #include "util/util_half.h"
27 #include "util/util_logging.h"
28 #include "util/util_math.h"
29 #include "util/util_opengl.h"
30 #include "util/util_string.h"
31 #include "util/util_system.h"
32 #include "util/util_time.h"
33 #include "util/util_types.h"
34 #include "util/util_vector.h"
35
36 CCL_NAMESPACE_BEGIN
37
38 bool Device::need_types_update = true;
39 bool Device::need_devices_update = true;
40 thread_mutex Device::device_mutex;
41 vector<DeviceInfo> Device::opencl_devices;
42 vector<DeviceInfo> Device::cuda_devices;
43 vector<DeviceInfo> Device::optix_devices;
44 vector<DeviceInfo> Device::cpu_devices;
45 vector<DeviceInfo> Device::network_devices;
46 uint Device::devices_initialized_mask = 0;
47
48 /* Device Requested Features */
49
50 std::ostream &operator<<(std::ostream &os, const DeviceRequestedFeatures &requested_features)
51 {
52   os << "Experimental features: " << (requested_features.experimental ? "On" : "Off") << std::endl;
53   os << "Max nodes group: " << requested_features.max_nodes_group << std::endl;
54   /* TODO(sergey): Decode bitflag into list of names. */
55   os << "Nodes features: " << requested_features.nodes_features << std::endl;
56   os << "Use Hair: " << string_from_bool(requested_features.use_hair) << std::endl;
57   os << "Use Object Motion: " << string_from_bool(requested_features.use_object_motion)
58      << std::endl;
59   os << "Use Camera Motion: " << string_from_bool(requested_features.use_camera_motion)
60      << std::endl;
61   os << "Use Baking: " << string_from_bool(requested_features.use_baking) << std::endl;
62   os << "Use Subsurface: " << string_from_bool(requested_features.use_subsurface) << std::endl;
63   os << "Use Volume: " << string_from_bool(requested_features.use_volume) << std::endl;
64   os << "Use Branched Integrator: " << string_from_bool(requested_features.use_integrator_branched)
65      << std::endl;
66   os << "Use Patch Evaluation: " << string_from_bool(requested_features.use_patch_evaluation)
67      << std::endl;
68   os << "Use Transparent Shadows: " << string_from_bool(requested_features.use_transparent)
69      << std::endl;
70   os << "Use Principled BSDF: " << string_from_bool(requested_features.use_principled)
71      << std::endl;
72   os << "Use Denoising: " << string_from_bool(requested_features.use_denoising) << std::endl;
73   os << "Use Displacement: " << string_from_bool(requested_features.use_true_displacement)
74      << std::endl;
75   os << "Use Background Light: " << string_from_bool(requested_features.use_background_light)
76      << std::endl;
77   return os;
78 }
79
80 /* Device */
81
82 Device::~Device() noexcept(false)
83 {
84   if (!background) {
85     if (vertex_buffer != 0) {
86       glDeleteBuffers(1, &vertex_buffer);
87     }
88     if (fallback_shader_program != 0) {
89       glDeleteProgram(fallback_shader_program);
90     }
91   }
92 }
93
94 /* TODO move shaders to standalone .glsl file. */
95 const char *FALLBACK_VERTEX_SHADER =
96     "#version 330\n"
97     "uniform vec2 fullscreen;\n"
98     "in vec2 texCoord;\n"
99     "in vec2 pos;\n"
100     "out vec2 texCoord_interp;\n"
101     "\n"
102     "vec2 normalize_coordinates()\n"
103     "{\n"
104     "   return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n"
105     "}\n"
106     "\n"
107     "void main()\n"
108     "{\n"
109     "   gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n"
110     "   texCoord_interp = texCoord;\n"
111     "}\n\0";
112
113 const char *FALLBACK_FRAGMENT_SHADER =
114     "#version 330\n"
115     "uniform sampler2D image_texture;\n"
116     "in vec2 texCoord_interp;\n"
117     "out vec4 fragColor;\n"
118     "\n"
119     "void main()\n"
120     "{\n"
121     "   fragColor = texture(image_texture, texCoord_interp);\n"
122     "}\n\0";
123
124 static void shader_print_errors(const char *task, const char *log, const char *code)
125 {
126   LOG(ERROR) << "Shader: " << task << " error:";
127   LOG(ERROR) << "===== shader string ====";
128
129   stringstream stream(code);
130   string partial;
131
132   int line = 1;
133   while (getline(stream, partial, '\n')) {
134     if (line < 10) {
135       LOG(ERROR) << " " << line << " " << partial;
136     }
137     else {
138       LOG(ERROR) << line << " " << partial;
139     }
140     line++;
141   }
142   LOG(ERROR) << log;
143 }
144
145 static int bind_fallback_shader(void)
146 {
147   GLint status;
148   GLchar log[5000];
149   GLsizei length = 0;
150   GLuint program = 0;
151
152   struct Shader {
153     const char *source;
154     GLenum type;
155   } shaders[2] = {{FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER},
156                   {FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}};
157
158   program = glCreateProgram();
159
160   for (int i = 0; i < 2; i++) {
161     GLuint shader = glCreateShader(shaders[i].type);
162
163     string source_str = shaders[i].source;
164     const char *c_str = source_str.c_str();
165
166     glShaderSource(shader, 1, &c_str, NULL);
167     glCompileShader(shader);
168
169     glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
170
171     if (!status) {
172       glGetShaderInfoLog(shader, sizeof(log), &length, log);
173       shader_print_errors("compile", log, c_str);
174       return 0;
175     }
176
177     glAttachShader(program, shader);
178   }
179
180   /* Link output. */
181   glBindFragDataLocation(program, 0, "fragColor");
182
183   /* Link and error check. */
184   glLinkProgram(program);
185
186   glGetProgramiv(program, GL_LINK_STATUS, &status);
187   if (!status) {
188     glGetShaderInfoLog(program, sizeof(log), &length, log);
189     shader_print_errors("linking", log, FALLBACK_VERTEX_SHADER);
190     shader_print_errors("linking", log, FALLBACK_FRAGMENT_SHADER);
191     return 0;
192   }
193
194   return program;
195 }
196
197 bool Device::bind_fallback_display_space_shader(const float width, const float height)
198 {
199   if (fallback_status == FALLBACK_SHADER_STATUS_ERROR) {
200     return false;
201   }
202
203   if (fallback_status == FALLBACK_SHADER_STATUS_NONE) {
204     fallback_shader_program = bind_fallback_shader();
205     fallback_status = FALLBACK_SHADER_STATUS_ERROR;
206
207     if (fallback_shader_program == 0) {
208       return false;
209     }
210
211     glUseProgram(fallback_shader_program);
212     image_texture_location = glGetUniformLocation(fallback_shader_program, "image_texture");
213     if (image_texture_location < 0) {
214       LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform.";
215       return false;
216     }
217
218     fullscreen_location = glGetUniformLocation(fallback_shader_program, "fullscreen");
219     if (fullscreen_location < 0) {
220       LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform.";
221       return false;
222     }
223
224     fallback_status = FALLBACK_SHADER_STATUS_SUCCESS;
225   }
226
227   /* Run this every time. */
228   glUseProgram(fallback_shader_program);
229   glUniform1i(image_texture_location, 0);
230   glUniform2f(fullscreen_location, width, height);
231   return true;
232 }
233
234 void Device::draw_pixels(device_memory &rgba,
235                          int y,
236                          int w,
237                          int h,
238                          int width,
239                          int height,
240                          int dx,
241                          int dy,
242                          int dw,
243                          int dh,
244                          bool transparent,
245                          const DeviceDrawParams &draw_params)
246 {
247   const bool use_fallback_shader = (draw_params.bind_display_space_shader_cb == NULL);
248
249   assert(rgba.type == MEM_PIXELS);
250   mem_copy_from(rgba, y, w, h, rgba.memory_elements_size(1));
251
252   GLuint texid;
253   glActiveTexture(GL_TEXTURE0);
254   glGenTextures(1, &texid);
255   glBindTexture(GL_TEXTURE_2D, texid);
256
257   if (rgba.data_type == TYPE_HALF) {
258     GLhalf *data_pointer = (GLhalf *)rgba.host_pointer;
259     data_pointer += 4 * y * w;
260     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_HALF_FLOAT, data_pointer);
261   }
262   else {
263     uint8_t *data_pointer = (uint8_t *)rgba.host_pointer;
264     data_pointer += 4 * y * w;
265     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data_pointer);
266   }
267
268   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
269   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
270
271   if (transparent) {
272     glEnable(GL_BLEND);
273     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
274   }
275
276   GLint shader_program;
277   if (use_fallback_shader) {
278     if (!bind_fallback_display_space_shader(dw, dh)) {
279       return;
280     }
281     shader_program = fallback_shader_program;
282   }
283   else {
284     draw_params.bind_display_space_shader_cb();
285     glGetIntegerv(GL_CURRENT_PROGRAM, &shader_program);
286   }
287
288   if (!vertex_buffer) {
289     glGenBuffers(1, &vertex_buffer);
290   }
291
292   glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
293   /* invalidate old contents - avoids stalling if buffer is still waiting in queue to be rendered
294    */
295   glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW);
296
297   float *vpointer = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
298
299   if (vpointer) {
300     /* texture coordinate - vertex pair */
301     vpointer[0] = 0.0f;
302     vpointer[1] = 0.0f;
303     vpointer[2] = dx;
304     vpointer[3] = dy;
305
306     vpointer[4] = 1.0f;
307     vpointer[5] = 0.0f;
308     vpointer[6] = (float)width + dx;
309     vpointer[7] = dy;
310
311     vpointer[8] = 1.0f;
312     vpointer[9] = 1.0f;
313     vpointer[10] = (float)width + dx;
314     vpointer[11] = (float)height + dy;
315
316     vpointer[12] = 0.0f;
317     vpointer[13] = 1.0f;
318     vpointer[14] = dx;
319     vpointer[15] = (float)height + dy;
320
321     if (vertex_buffer) {
322       glUnmapBuffer(GL_ARRAY_BUFFER);
323     }
324   }
325
326   GLuint vertex_array_object;
327   GLuint position_attribute, texcoord_attribute;
328
329   glGenVertexArrays(1, &vertex_array_object);
330   glBindVertexArray(vertex_array_object);
331
332   texcoord_attribute = glGetAttribLocation(shader_program, "texCoord");
333   position_attribute = glGetAttribLocation(shader_program, "pos");
334
335   glEnableVertexAttribArray(texcoord_attribute);
336   glEnableVertexAttribArray(position_attribute);
337
338   glVertexAttribPointer(
339       texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0);
340   glVertexAttribPointer(position_attribute,
341                         2,
342                         GL_FLOAT,
343                         GL_FALSE,
344                         4 * sizeof(float),
345                         (const GLvoid *)(sizeof(float) * 2));
346
347   glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
348
349   if (vertex_buffer) {
350     glBindBuffer(GL_ARRAY_BUFFER, 0);
351   }
352
353   if (use_fallback_shader) {
354     glUseProgram(0);
355   }
356   else {
357     draw_params.unbind_display_space_shader_cb();
358   }
359
360   glDeleteVertexArrays(1, &vertex_array_object);
361   glBindTexture(GL_TEXTURE_2D, 0);
362   glDeleteTextures(1, &texid);
363
364   if (transparent) {
365     glDisable(GL_BLEND);
366   }
367 }
368
369 void Device::build_bvh(BVH *bvh, Progress &progress, bool refit)
370 {
371   assert(bvh->params.bvh_layout == BVH_LAYOUT_BVH2);
372
373   BVH2 *const bvh2 = static_cast<BVH2 *>(bvh);
374   if (refit) {
375     bvh2->refit(progress);
376   }
377   else {
378     bvh2->build(progress, &stats);
379   }
380 }
381
382 Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background)
383 {
384 #ifdef WITH_MULTI
385   if (!info.multi_devices.empty()) {
386     /* Always create a multi device when info contains multiple devices.
387      * This is done so that the type can still be e.g. DEVICE_CPU to indicate
388      * that it is a homogeneous collection of devices, which simplifies checks. */
389     return device_multi_create(info, stats, profiler, background);
390   }
391 #endif
392
393   Device *device = NULL;
394
395   switch (info.type) {
396     case DEVICE_CPU:
397       device = device_cpu_create(info, stats, profiler, background);
398       break;
399 #ifdef WITH_CUDA
400     case DEVICE_CUDA:
401       if (device_cuda_init())
402         device = device_cuda_create(info, stats, profiler, background);
403       break;
404 #endif
405 #ifdef WITH_OPTIX
406     case DEVICE_OPTIX:
407       if (device_optix_init())
408         device = device_optix_create(info, stats, profiler, background);
409       break;
410 #endif
411 #ifdef WITH_NETWORK
412     case DEVICE_NETWORK:
413       device = device_network_create(info, stats, profiler, "127.0.0.1");
414       break;
415 #endif
416 #ifdef WITH_OPENCL
417     case DEVICE_OPENCL:
418       if (device_opencl_init())
419         device = device_opencl_create(info, stats, profiler, background);
420       break;
421 #endif
422     default:
423       break;
424   }
425
426   if (device == NULL) {
427     device = device_dummy_create(info, stats, profiler, background);
428   }
429
430   return device;
431 }
432
433 DeviceType Device::type_from_string(const char *name)
434 {
435   if (strcmp(name, "CPU") == 0)
436     return DEVICE_CPU;
437   else if (strcmp(name, "CUDA") == 0)
438     return DEVICE_CUDA;
439   else if (strcmp(name, "OPTIX") == 0)
440     return DEVICE_OPTIX;
441   else if (strcmp(name, "OPENCL") == 0)
442     return DEVICE_OPENCL;
443   else if (strcmp(name, "NETWORK") == 0)
444     return DEVICE_NETWORK;
445   else if (strcmp(name, "MULTI") == 0)
446     return DEVICE_MULTI;
447
448   return DEVICE_NONE;
449 }
450
451 string Device::string_from_type(DeviceType type)
452 {
453   if (type == DEVICE_CPU)
454     return "CPU";
455   else if (type == DEVICE_CUDA)
456     return "CUDA";
457   else if (type == DEVICE_OPTIX)
458     return "OPTIX";
459   else if (type == DEVICE_OPENCL)
460     return "OPENCL";
461   else if (type == DEVICE_NETWORK)
462     return "NETWORK";
463   else if (type == DEVICE_MULTI)
464     return "MULTI";
465
466   return "";
467 }
468
469 vector<DeviceType> Device::available_types()
470 {
471   vector<DeviceType> types;
472   types.push_back(DEVICE_CPU);
473 #ifdef WITH_CUDA
474   types.push_back(DEVICE_CUDA);
475 #endif
476 #ifdef WITH_OPTIX
477   types.push_back(DEVICE_OPTIX);
478 #endif
479 #ifdef WITH_OPENCL
480   types.push_back(DEVICE_OPENCL);
481 #endif
482 #ifdef WITH_NETWORK
483   types.push_back(DEVICE_NETWORK);
484 #endif
485   return types;
486 }
487
488 vector<DeviceInfo> Device::available_devices(uint mask)
489 {
490   /* Lazy initialize devices. On some platforms OpenCL or CUDA drivers can
491    * be broken and cause crashes when only trying to get device info, so
492    * we don't want to do any initialization until the user chooses to. */
493   thread_scoped_lock lock(device_mutex);
494   vector<DeviceInfo> devices;
495
496 #ifdef WITH_OPENCL
497   if (mask & DEVICE_MASK_OPENCL) {
498     if (!(devices_initialized_mask & DEVICE_MASK_OPENCL)) {
499       if (device_opencl_init()) {
500         device_opencl_info(opencl_devices);
501       }
502       devices_initialized_mask |= DEVICE_MASK_OPENCL;
503     }
504     foreach (DeviceInfo &info, opencl_devices) {
505       devices.push_back(info);
506     }
507   }
508 #endif
509
510 #if defined(WITH_CUDA) || defined(WITH_OPTIX)
511   if (mask & (DEVICE_MASK_CUDA | DEVICE_MASK_OPTIX)) {
512     if (!(devices_initialized_mask & DEVICE_MASK_CUDA)) {
513       if (device_cuda_init()) {
514         device_cuda_info(cuda_devices);
515       }
516       devices_initialized_mask |= DEVICE_MASK_CUDA;
517     }
518     if (mask & DEVICE_MASK_CUDA) {
519       foreach (DeviceInfo &info, cuda_devices) {
520         devices.push_back(info);
521       }
522     }
523   }
524 #endif
525
526 #ifdef WITH_OPTIX
527   if (mask & DEVICE_MASK_OPTIX) {
528     if (!(devices_initialized_mask & DEVICE_MASK_OPTIX)) {
529       if (device_optix_init()) {
530         device_optix_info(cuda_devices, optix_devices);
531       }
532       devices_initialized_mask |= DEVICE_MASK_OPTIX;
533     }
534     foreach (DeviceInfo &info, optix_devices) {
535       devices.push_back(info);
536     }
537   }
538 #endif
539
540   if (mask & DEVICE_MASK_CPU) {
541     if (!(devices_initialized_mask & DEVICE_MASK_CPU)) {
542       device_cpu_info(cpu_devices);
543       devices_initialized_mask |= DEVICE_MASK_CPU;
544     }
545     foreach (DeviceInfo &info, cpu_devices) {
546       devices.push_back(info);
547     }
548   }
549
550 #ifdef WITH_NETWORK
551   if (mask & DEVICE_MASK_NETWORK) {
552     if (!(devices_initialized_mask & DEVICE_MASK_NETWORK)) {
553       device_network_info(network_devices);
554       devices_initialized_mask |= DEVICE_MASK_NETWORK;
555     }
556     foreach (DeviceInfo &info, network_devices) {
557       devices.push_back(info);
558     }
559   }
560 #endif
561
562   return devices;
563 }
564
565 DeviceInfo Device::dummy_device(const string &error_msg)
566 {
567   DeviceInfo info;
568   info.type = DEVICE_DUMMY;
569   info.error_msg = error_msg;
570   return info;
571 }
572
573 string Device::device_capabilities(uint mask)
574 {
575   thread_scoped_lock lock(device_mutex);
576   string capabilities = "";
577
578   if (mask & DEVICE_MASK_CPU) {
579     capabilities += "\nCPU device capabilities: ";
580     capabilities += device_cpu_capabilities() + "\n";
581   }
582
583 #ifdef WITH_OPENCL
584   if (mask & DEVICE_MASK_OPENCL) {
585     if (device_opencl_init()) {
586       capabilities += "\nOpenCL device capabilities:\n";
587       capabilities += device_opencl_capabilities();
588     }
589   }
590 #endif
591
592 #ifdef WITH_CUDA
593   if (mask & DEVICE_MASK_CUDA) {
594     if (device_cuda_init()) {
595       capabilities += "\nCUDA device capabilities:\n";
596       capabilities += device_cuda_capabilities();
597     }
598   }
599 #endif
600
601   return capabilities;
602 }
603
604 DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
605                                     int threads,
606                                     bool background)
607 {
608   assert(subdevices.size() > 0);
609
610   if (subdevices.size() == 1) {
611     /* No multi device needed. */
612     return subdevices.front();
613   }
614
615   DeviceInfo info;
616   info.type = subdevices.front().type;
617   info.id = "MULTI";
618   info.description = "Multi Device";
619   info.num = 0;
620
621   info.has_half_images = true;
622   info.has_nanovdb = true;
623   info.has_volume_decoupled = true;
624   info.has_branched_path = true;
625   info.has_adaptive_stop_per_sample = true;
626   info.has_osl = true;
627   info.has_profiling = true;
628   info.has_peer_memory = false;
629   info.denoisers = DENOISER_ALL;
630
631   foreach (const DeviceInfo &device, subdevices) {
632     /* Ensure CPU device does not slow down GPU. */
633     if (device.type == DEVICE_CPU && subdevices.size() > 1) {
634       if (background) {
635         int orig_cpu_threads = (threads) ? threads : system_cpu_thread_count();
636         int cpu_threads = max(orig_cpu_threads - (subdevices.size() - 1), 0);
637
638         VLOG(1) << "CPU render threads reduced from " << orig_cpu_threads << " to " << cpu_threads
639                 << ", to dedicate to GPU.";
640
641         if (cpu_threads >= 1) {
642           DeviceInfo cpu_device = device;
643           cpu_device.cpu_threads = cpu_threads;
644           info.multi_devices.push_back(cpu_device);
645         }
646         else {
647           continue;
648         }
649       }
650       else {
651         VLOG(1) << "CPU render threads disabled for interactive render.";
652         continue;
653       }
654     }
655     else {
656       info.multi_devices.push_back(device);
657     }
658
659     /* Create unique ID for this combination of devices. */
660     info.id += device.id;
661
662     /* Set device type to MULTI if subdevices are not of a common type. */
663     if (device.type != info.type) {
664       info.type = DEVICE_MULTI;
665     }
666
667     /* Accumulate device info. */
668     info.has_half_images &= device.has_half_images;
669     info.has_nanovdb &= device.has_nanovdb;
670     info.has_volume_decoupled &= device.has_volume_decoupled;
671     info.has_branched_path &= device.has_branched_path;
672     info.has_adaptive_stop_per_sample &= device.has_adaptive_stop_per_sample;
673     info.has_osl &= device.has_osl;
674     info.has_profiling &= device.has_profiling;
675     info.has_peer_memory |= device.has_peer_memory;
676     info.denoisers &= device.denoisers;
677   }
678
679   return info;
680 }
681
682 void Device::tag_update()
683 {
684   free_memory();
685 }
686
687 void Device::free_memory()
688 {
689   devices_initialized_mask = 0;
690   cuda_devices.free_memory();
691   optix_devices.free_memory();
692   opencl_devices.free_memory();
693   cpu_devices.free_memory();
694   network_devices.free_memory();
695 }
696
697 /* DeviceInfo */
698
699 void DeviceInfo::add_denoising_devices(DenoiserType denoiser_type)
700 {
701   assert(denoising_devices.empty());
702
703   if (denoiser_type == DENOISER_OPTIX && type != DEVICE_OPTIX) {
704     vector<DeviceInfo> optix_devices = Device::available_devices(DEVICE_MASK_OPTIX);
705     if (!optix_devices.empty()) {
706       /* Convert to a special multi device with separate denoising devices. */
707       if (multi_devices.empty()) {
708         multi_devices.push_back(*this);
709       }
710
711       /* Try to use the same physical devices for denoising. */
712       for (const DeviceInfo &cuda_device : multi_devices) {
713         if (cuda_device.type == DEVICE_CUDA) {
714           for (const DeviceInfo &optix_device : optix_devices) {
715             if (cuda_device.num == optix_device.num) {
716               id += optix_device.id;
717               denoising_devices.push_back(optix_device);
718               break;
719             }
720           }
721         }
722       }
723
724       if (denoising_devices.empty()) {
725         /* Simply use the first available OptiX device. */
726         const DeviceInfo optix_device = optix_devices.front();
727         id += optix_device.id; /* Uniquely identify this special multi device. */
728         denoising_devices.push_back(optix_device);
729       }
730
731       denoisers = denoiser_type;
732     }
733   }
734   else if (denoiser_type == DENOISER_OPENIMAGEDENOISE && type != DEVICE_CPU) {
735     /* Convert to a special multi device with separate denoising devices. */
736     if (multi_devices.empty()) {
737       multi_devices.push_back(*this);
738     }
739
740     /* Add CPU denoising devices. */
741     DeviceInfo cpu_device = Device::available_devices(DEVICE_MASK_CPU).front();
742     denoising_devices.push_back(cpu_device);
743
744     denoisers = denoiser_type;
745   }
746 }
747
748 CCL_NAMESPACE_END