Cycles: enable multi closure sampling and transparent shadows only on CPU and
[blender.git] / intern / cycles / device / device_cuda.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 <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "device.h"
24 #include "device_intern.h"
25
26 #include "util_cuda.h"
27 #include "util_debug.h"
28 #include "util_map.h"
29 #include "util_opengl.h"
30 #include "util_path.h"
31 #include "util_system.h"
32 #include "util_types.h"
33 #include "util_time.h"
34
35 CCL_NAMESPACE_BEGIN
36
37 class CUDADevice : public Device
38 {
39 public:
40         CUdevice cuDevice;
41         CUcontext cuContext;
42         CUmodule cuModule;
43         map<device_ptr, bool> tex_interp_map;
44         int cuDevId;
45
46         struct PixelMem {
47                 GLuint cuPBO;
48                 CUgraphicsResource cuPBOresource;
49                 GLuint cuTexId;
50                 int w, h;
51         };
52
53         map<device_ptr, PixelMem> pixel_mem_map;
54
55         CUdeviceptr cuda_device_ptr(device_ptr mem)
56         {
57                 return (CUdeviceptr)mem;
58         }
59
60         const char *cuda_error_string(CUresult result)
61         {
62                 switch(result) {
63                         case CUDA_SUCCESS: return "No errors";
64                         case CUDA_ERROR_INVALID_VALUE: return "Invalid value";
65                         case CUDA_ERROR_OUT_OF_MEMORY: return "Out of memory";
66                         case CUDA_ERROR_NOT_INITIALIZED: return "Driver not initialized";
67                         case CUDA_ERROR_DEINITIALIZED: return "Driver deinitialized";
68
69                         case CUDA_ERROR_NO_DEVICE: return "No CUDA-capable device available";
70                         case CUDA_ERROR_INVALID_DEVICE: return "Invalid device";
71
72                         case CUDA_ERROR_INVALID_IMAGE: return "Invalid kernel image";
73                         case CUDA_ERROR_INVALID_CONTEXT: return "Invalid context";
74                         case CUDA_ERROR_CONTEXT_ALREADY_CURRENT: return "Context already current";
75                         case CUDA_ERROR_MAP_FAILED: return "Map failed";
76                         case CUDA_ERROR_UNMAP_FAILED: return "Unmap failed";
77                         case CUDA_ERROR_ARRAY_IS_MAPPED: return "Array is mapped";
78                         case CUDA_ERROR_ALREADY_MAPPED: return "Already mapped";
79                         case CUDA_ERROR_NO_BINARY_FOR_GPU: return "No binary for GPU";
80                         case CUDA_ERROR_ALREADY_ACQUIRED: return "Already acquired";
81                         case CUDA_ERROR_NOT_MAPPED: return "Not mapped";
82                         case CUDA_ERROR_NOT_MAPPED_AS_ARRAY: return "Mapped resource not available for access as an array";
83                         case CUDA_ERROR_NOT_MAPPED_AS_POINTER: return "Mapped resource not available for access as a pointer";
84                         case CUDA_ERROR_ECC_UNCORRECTABLE: return "Uncorrectable ECC error detected";
85                         case CUDA_ERROR_UNSUPPORTED_LIMIT: return "CUlimit not supported by device";
86
87                         case CUDA_ERROR_INVALID_SOURCE: return "Invalid source";
88                         case CUDA_ERROR_FILE_NOT_FOUND: return "File not found";
89                         case CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND: return "Link to a shared object failed to resolve";
90                         case CUDA_ERROR_SHARED_OBJECT_INIT_FAILED: return "Shared object initialization failed";
91
92                         case CUDA_ERROR_INVALID_HANDLE: return "Invalid handle";
93
94                         case CUDA_ERROR_NOT_FOUND: return "Not found";
95
96                         case CUDA_ERROR_NOT_READY: return "CUDA not ready";
97
98                         case CUDA_ERROR_LAUNCH_FAILED: return "Launch failed";
99                         case CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES: return "Launch exceeded resources";
100                         case CUDA_ERROR_LAUNCH_TIMEOUT: return "Launch exceeded timeout";
101                         case CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING: return "Launch with incompatible texturing";
102
103                         case CUDA_ERROR_UNKNOWN: return "Unknown error";
104
105                         default: return "Unknown CUDA error value";
106                 }
107         }
108
109         static int cuda_align_up(int& offset, int alignment)
110         {
111                 return (offset + alignment - 1) & ~(alignment - 1);
112         }
113
114 #ifdef NDEBUG
115 #define cuda_abort()
116 #else
117 #define cuda_abort() abort()
118 #endif
119
120 #define cuda_assert(stmt) \
121         { \
122                 CUresult result = stmt; \
123                 \
124                 if(result != CUDA_SUCCESS) { \
125                         fprintf(stderr, "CUDA error: %s in %s\n", cuda_error_string(result), #stmt); \
126                         cuda_abort(); \
127                 } \
128         }
129
130         bool cuda_error(CUresult result)
131         {
132                 if(result == CUDA_SUCCESS)
133                         return false;
134
135                 fprintf(stderr, "CUDA error: %s\n", cuda_error_string(result));
136                 return true;
137         }
138
139         void cuda_push_context()
140         {
141                 cuda_assert(cuCtxSetCurrent(cuContext))
142         }
143
144         void cuda_pop_context()
145         {
146                 cuda_assert(cuCtxSetCurrent(NULL));
147         }
148
149         CUDADevice(bool background_)
150         {
151                 background = background_;
152
153                 cuDevId = 0;
154                 cuDevice = 0;
155                 cuContext = 0;
156
157                 /* intialize */
158                 if(cuda_error(cuInit(0)))
159                         return;
160
161                 /* setup device and context */
162                 if(cuda_error(cuDeviceGet(&cuDevice, cuDevId)))
163                         return;
164
165                 CUresult result;
166
167                 if(background)
168                         result = cuCtxCreate(&cuContext, 0, cuDevice);
169                 else
170                         result = cuGLCtxCreate(&cuContext, 0, cuDevice);
171
172                 if(cuda_error(result))
173                         return;
174
175                 cuda_pop_context();
176         }
177
178         ~CUDADevice()
179         {
180                 cuda_push_context();
181                 cuda_assert(cuCtxDetach(cuContext))
182         }
183
184         bool support_full_kernel()
185         {
186                 int major, minor;
187                 cuDeviceComputeCapability(&major, &minor, cuDevId);
188
189                 return (major >= 2);
190         }
191
192         string description()
193         {
194                 /* print device information */
195                 char deviceName[100];
196
197                 cuda_push_context();
198                 cuDeviceGetName(deviceName, 256, cuDevId);
199                 cuda_pop_context();
200
201                 return string("CUDA ") + deviceName;
202         }
203
204         string compile_kernel()
205         {
206                 /* compute cubin name */
207                 int major, minor;
208                 cuDeviceComputeCapability(&major, &minor, cuDevId);
209
210                 /* attempt to use kernel provided with blender */
211                 string cubin = path_get(string_printf("lib/kernel_sm_%d%d.cubin", major, minor));
212                 if(path_exists(cubin))
213                         return cubin;
214
215                 /* not found, try to use locally compiled kernel */
216                 string kernel_path = path_get("kernel");
217                 string md5 = path_files_md5_hash(kernel_path);
218
219                 cubin = string_printf("cycles_kernel_sm%d%d_%s.cubin", major, minor, md5.c_str());;
220                 cubin = path_user_get(path_join("cache", cubin));
221
222                 /* if exists already, use it */
223                 if(path_exists(cubin))
224                         return cubin;
225
226                 /* if not, find CUDA compiler */
227                 string nvcc = cuCompilerPath();
228
229                 if(nvcc == "") {
230                         fprintf(stderr, "CUDA nvcc compiler not found. Install CUDA toolkit in default location.\n");
231                         return "";
232                 }
233
234                 /* compile */
235                 string kernel = path_join(kernel_path, "kernel.cu");
236                 string include = kernel_path;
237                 const int machine = system_cpu_bits();
238                 const int maxreg = 24;
239
240                 double starttime = time_dt();
241                 printf("Compiling CUDA kernel ...\n");
242
243                 path_create_directories(cubin);
244
245                 string command = string_printf("%s -arch=sm_%d%d -m%d --cubin \"%s\" --use_fast_math "
246                         "-o \"%s\" --ptxas-options=\"-v\" --maxrregcount=%d --opencc-options -OPT:Olimit=0 -I\"%s\" -DNVCC",
247                         nvcc.c_str(), major, minor, machine, kernel.c_str(), cubin.c_str(), maxreg, include.c_str());
248
249                 if(system(command.c_str()) == -1) {
250                         fprintf(stderr, "Failed to execute compilation command.\n");
251                         return "";
252                 }
253
254                 /* verify if compilation succeeded */
255                 if(!path_exists(cubin)) {
256                         fprintf(stderr, "CUDA kernel compilation failed.\n");
257                         return "";
258                 }
259
260                 printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime);
261
262                 return cubin;
263         }
264
265         bool load_kernels()
266         {
267                 /* check if cuda init succeeded */
268                 if(cuContext == 0)
269                         return false;
270
271                 /* get kernel */
272                 string cubin = compile_kernel();
273
274                 if(cubin == "")
275                         return false;
276
277                 /* open module */
278                 cuda_push_context();
279
280                 CUresult result = cuModuleLoad(&cuModule, cubin.c_str());
281                 if(cuda_error(result))
282                         fprintf(stderr, "Failed loading CUDA kernel %s.\n", cubin.c_str());
283
284                 cuda_pop_context();
285
286                 return (result == CUDA_SUCCESS);
287         }
288
289         void mem_alloc(device_memory& mem, MemoryType type)
290         {
291                 cuda_push_context();
292                 CUdeviceptr device_pointer;
293                 cuda_assert(cuMemAlloc(&device_pointer, mem.memory_size()))
294                 mem.device_pointer = (device_ptr)device_pointer;
295                 cuda_pop_context();
296         }
297
298         void mem_copy_to(device_memory& mem)
299         {
300                 cuda_push_context();
301                 cuda_assert(cuMemcpyHtoD(cuda_device_ptr(mem.device_pointer), (void*)mem.data_pointer, mem.memory_size()))
302                 cuda_pop_context();
303         }
304
305         void mem_copy_from(device_memory& mem, size_t offset, size_t size)
306         {
307                 /* todo: offset is ignored */
308                 cuda_push_context();
309                 cuda_assert(cuMemcpyDtoH((uchar*)mem.data_pointer + offset,
310                         (CUdeviceptr)((uchar*)mem.device_pointer + offset), size))
311                 cuda_pop_context();
312         }
313
314         void mem_zero(device_memory& mem)
315         {
316                 memset((void*)mem.data_pointer, 0, mem.memory_size());
317
318                 cuda_push_context();
319                 cuda_assert(cuMemsetD8(cuda_device_ptr(mem.device_pointer), 0, mem.memory_size()))
320                 cuda_pop_context();
321         }
322
323         void mem_free(device_memory& mem)
324         {
325                 if(mem.device_pointer) {
326                         cuda_push_context();
327                         cuda_assert(cuMemFree(cuda_device_ptr(mem.device_pointer)))
328                         cuda_pop_context();
329
330                         mem.device_pointer = 0;
331                 }
332         }
333
334         void const_copy_to(const char *name, void *host, size_t size)
335         {
336                 CUdeviceptr mem;
337                 size_t bytes;
338
339                 cuda_push_context();
340                 cuda_assert(cuModuleGetGlobal(&mem, &bytes, cuModule, name))
341                 //assert(bytes == size);
342                 cuda_assert(cuMemcpyHtoD(mem, host, size))
343                 cuda_pop_context();
344         }
345
346         void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
347         {
348                 /* determine format */
349                 CUarray_format_enum format;
350                 size_t dsize = datatype_size(mem.data_type);
351                 size_t size = mem.memory_size();
352
353                 switch(mem.data_type) {
354                         case TYPE_UCHAR: format = CU_AD_FORMAT_UNSIGNED_INT8; break;
355                         case TYPE_UINT: format = CU_AD_FORMAT_UNSIGNED_INT32; break;
356                         case TYPE_INT: format = CU_AD_FORMAT_SIGNED_INT32; break;
357                         case TYPE_FLOAT: format = CU_AD_FORMAT_FLOAT; break;
358                         default: assert(0); return;
359                 }
360
361                 CUtexref texref;
362
363                 cuda_push_context();
364                 cuda_assert(cuModuleGetTexRef(&texref, cuModule, name))
365
366                 if(interpolation) {
367                         CUarray handle;
368                         CUDA_ARRAY_DESCRIPTOR desc;
369
370                         desc.Width = mem.data_width;
371                         desc.Height = mem.data_height;
372                         desc.Format = format;
373                         desc.NumChannels = mem.data_elements;
374
375                         cuda_assert(cuArrayCreate(&handle, &desc))
376
377                         if(mem.data_height > 1) {
378                                 CUDA_MEMCPY2D param;
379                                 memset(&param, 0, sizeof(param));
380                                 param.dstMemoryType = CU_MEMORYTYPE_ARRAY;
381                                 param.dstArray = handle;
382                                 param.srcMemoryType = CU_MEMORYTYPE_HOST;
383                                 param.srcHost = (void*)mem.data_pointer;
384                                 param.srcPitch = mem.data_width*dsize*mem.data_elements;
385                                 param.WidthInBytes = param.srcPitch;
386                                 param.Height = mem.data_height;
387
388                                 cuda_assert(cuMemcpy2D(&param))
389                         }
390                         else
391                                 cuda_assert(cuMemcpyHtoA(handle, 0, (void*)mem.data_pointer, size))
392
393                         cuda_assert(cuTexRefSetArray(texref, handle, CU_TRSA_OVERRIDE_FORMAT))
394
395                         cuda_assert(cuTexRefSetFilterMode(texref, CU_TR_FILTER_MODE_LINEAR))
396                         cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_NORMALIZED_COORDINATES))
397
398                         mem.device_pointer = (device_ptr)handle;
399                 }
400                 else {
401                         cuda_pop_context();
402
403                         mem_alloc(mem, MEM_READ_ONLY);
404                         mem_copy_to(mem);
405
406                         cuda_push_context();
407
408                         cuda_assert(cuTexRefSetAddress(NULL, texref, cuda_device_ptr(mem.device_pointer), size))
409                         cuda_assert(cuTexRefSetFilterMode(texref, CU_TR_FILTER_MODE_POINT))
410                         cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_READ_AS_INTEGER))
411                 }
412
413                 if(periodic) {
414                         cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_WRAP))
415                         cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_WRAP))
416                 }
417                 else {
418                         cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_CLAMP))
419                         cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_CLAMP))
420                 }
421                 cuda_assert(cuTexRefSetFormat(texref, format, mem.data_elements))
422
423                 cuda_pop_context();
424
425                 tex_interp_map[mem.device_pointer] = interpolation;
426         }
427
428         void tex_free(device_memory& mem)
429         {
430                 if(mem.device_pointer) {
431                         if(tex_interp_map[mem.device_pointer]) {
432                                 cuda_push_context();
433                                 cuArrayDestroy((CUarray)mem.device_pointer);
434                                 cuda_pop_context();
435
436                                 tex_interp_map.erase(tex_interp_map.find(mem.device_pointer));
437                                 mem.device_pointer = 0;
438                         }
439                         else {
440                                 tex_interp_map.erase(tex_interp_map.find(mem.device_pointer));
441                                 mem_free(mem);
442                         }
443                 }
444         }
445
446         void path_trace(DeviceTask& task)
447         {
448                 cuda_push_context();
449
450                 CUfunction cuPathTrace;
451                 CUdeviceptr d_buffer = cuda_device_ptr(task.buffer);
452                 CUdeviceptr d_rng_state = cuda_device_ptr(task.rng_state);
453
454                 /* get kernel function */
455                 cuda_assert(cuModuleGetFunction(&cuPathTrace, cuModule, "kernel_cuda_path_trace"))
456                 
457                 /* pass in parameters */
458                 int offset = 0;
459                 
460                 cuda_assert(cuParamSetv(cuPathTrace, offset, &d_buffer, sizeof(d_buffer)))
461                 offset += sizeof(d_buffer);
462
463                 cuda_assert(cuParamSetv(cuPathTrace, offset, &d_rng_state, sizeof(d_rng_state)))
464                 offset += sizeof(d_rng_state);
465
466                 int sample = task.sample;
467                 offset = cuda_align_up(offset, __alignof(sample));
468
469                 cuda_assert(cuParamSeti(cuPathTrace, offset, task.sample))
470                 offset += sizeof(task.sample);
471
472                 cuda_assert(cuParamSeti(cuPathTrace, offset, task.x))
473                 offset += sizeof(task.x);
474
475                 cuda_assert(cuParamSeti(cuPathTrace, offset, task.y))
476                 offset += sizeof(task.y);
477
478                 cuda_assert(cuParamSeti(cuPathTrace, offset, task.w))
479                 offset += sizeof(task.w);
480
481                 cuda_assert(cuParamSeti(cuPathTrace, offset, task.h))
482                 offset += sizeof(task.h);
483
484                 cuda_assert(cuParamSetSize(cuPathTrace, offset))
485
486                 /* launch kernel: todo find optimal size, cache config for fermi */
487 #ifndef __APPLE__
488                 int xthreads = 16;
489                 int ythreads = 16;
490 #else
491                 int xthreads = 8;
492                 int ythreads = 8;
493 #endif
494                 int xblocks = (task.w + xthreads - 1)/xthreads;
495                 int yblocks = (task.h + ythreads - 1)/ythreads;
496
497                 cuda_assert(cuFuncSetCacheConfig(cuPathTrace, CU_FUNC_CACHE_PREFER_L1))
498                 cuda_assert(cuFuncSetBlockShape(cuPathTrace, xthreads, ythreads, 1))
499                 cuda_assert(cuLaunchGrid(cuPathTrace, xblocks, yblocks))
500
501                 cuda_pop_context();
502         }
503
504         void tonemap(DeviceTask& task)
505         {
506                 cuda_push_context();
507
508                 CUfunction cuFilmConvert;
509                 CUdeviceptr d_rgba = map_pixels(task.rgba);
510                 CUdeviceptr d_buffer = cuda_device_ptr(task.buffer);
511
512                 /* get kernel function */
513                 cuda_assert(cuModuleGetFunction(&cuFilmConvert, cuModule, "kernel_cuda_tonemap"))
514
515                 /* pass in parameters */
516                 int offset = 0;
517
518                 cuda_assert(cuParamSetv(cuFilmConvert, offset, &d_rgba, sizeof(d_rgba)))
519                 offset += sizeof(d_rgba);
520                 
521                 cuda_assert(cuParamSetv(cuFilmConvert, offset, &d_buffer, sizeof(d_buffer)))
522                 offset += sizeof(d_buffer);
523
524                 int sample = task.sample;
525                 offset = cuda_align_up(offset, __alignof(sample));
526
527                 cuda_assert(cuParamSeti(cuFilmConvert, offset, task.sample))
528                 offset += sizeof(task.sample);
529
530                 cuda_assert(cuParamSeti(cuFilmConvert, offset, task.resolution))
531                 offset += sizeof(task.resolution);
532
533                 cuda_assert(cuParamSeti(cuFilmConvert, offset, task.x))
534                 offset += sizeof(task.x);
535
536                 cuda_assert(cuParamSeti(cuFilmConvert, offset, task.y))
537                 offset += sizeof(task.y);
538
539                 cuda_assert(cuParamSeti(cuFilmConvert, offset, task.w))
540                 offset += sizeof(task.w);
541
542                 cuda_assert(cuParamSeti(cuFilmConvert, offset, task.h))
543                 offset += sizeof(task.h);
544
545                 cuda_assert(cuParamSetSize(cuFilmConvert, offset))
546
547                 /* launch kernel: todo find optimal size, cache config for fermi */
548 #ifndef __APPLE__
549                 int xthreads = 16;
550                 int ythreads = 16;
551 #else
552                 int xthreads = 8;
553                 int ythreads = 8;
554 #endif
555                 int xblocks = (task.w + xthreads - 1)/xthreads;
556                 int yblocks = (task.h + ythreads - 1)/ythreads;
557
558                 cuda_assert(cuFuncSetCacheConfig(cuFilmConvert, CU_FUNC_CACHE_PREFER_L1))
559                 cuda_assert(cuFuncSetBlockShape(cuFilmConvert, xthreads, ythreads, 1))
560                 cuda_assert(cuLaunchGrid(cuFilmConvert, xblocks, yblocks))
561
562                 unmap_pixels(task.rgba);
563
564                 cuda_pop_context();
565         }
566
567         void displace(DeviceTask& task)
568         {
569                 cuda_push_context();
570
571                 CUfunction cuDisplace;
572                 CUdeviceptr d_input = cuda_device_ptr(task.displace_input);
573                 CUdeviceptr d_offset = cuda_device_ptr(task.displace_offset);
574
575                 /* get kernel function */
576                 cuda_assert(cuModuleGetFunction(&cuDisplace, cuModule, "kernel_cuda_displace"))
577                 
578                 /* pass in parameters */
579                 int offset = 0;
580                 
581                 cuda_assert(cuParamSetv(cuDisplace, offset, &d_input, sizeof(d_input)))
582                 offset += sizeof(d_input);
583
584                 cuda_assert(cuParamSetv(cuDisplace, offset, &d_offset, sizeof(d_offset)))
585                 offset += sizeof(d_offset);
586
587                 int displace_x = task.displace_x;
588                 offset = cuda_align_up(offset, __alignof(displace_x));
589
590                 cuda_assert(cuParamSeti(cuDisplace, offset, task.displace_x))
591                 offset += sizeof(task.displace_x);
592
593                 cuda_assert(cuParamSetSize(cuDisplace, offset))
594
595                 /* launch kernel: todo find optimal size, cache config for fermi */
596 #ifndef __APPLE__
597                 int xthreads = 16;
598 #else
599                 int xthreads = 8;
600 #endif
601                 int xblocks = (task.displace_w + xthreads - 1)/xthreads;
602
603                 cuda_assert(cuFuncSetCacheConfig(cuDisplace, CU_FUNC_CACHE_PREFER_L1))
604                 cuda_assert(cuFuncSetBlockShape(cuDisplace, xthreads, 1, 1))
605                 cuda_assert(cuLaunchGrid(cuDisplace, xblocks, 1))
606
607                 cuda_pop_context();
608         }
609
610         CUdeviceptr map_pixels(device_ptr mem)
611         {
612                 if(!background) {
613                         PixelMem pmem = pixel_mem_map[mem];
614                         CUdeviceptr buffer;
615                         
616                         size_t bytes;
617                         cuda_assert(cuGraphicsMapResources(1, &pmem.cuPBOresource, 0))
618                         cuda_assert(cuGraphicsResourceGetMappedPointer(&buffer, &bytes, pmem.cuPBOresource))
619                         
620                         return buffer;
621                 }
622
623                 return cuda_device_ptr(mem);
624         }
625
626         void unmap_pixels(device_ptr mem)
627         {
628                 if(!background) {
629                         PixelMem pmem = pixel_mem_map[mem];
630
631                         cuda_assert(cuGraphicsUnmapResources(1, &pmem.cuPBOresource, 0))
632                 }
633         }
634
635         void pixels_alloc(device_memory& mem)
636         {
637                 if(!background) {
638                         PixelMem pmem;
639
640                         pmem.w = mem.data_width;
641                         pmem.h = mem.data_height;
642
643                         cuda_push_context();
644
645                         glGenBuffers(1, &pmem.cuPBO);
646                         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO);
647                         glBufferData(GL_PIXEL_UNPACK_BUFFER, pmem.w*pmem.h*sizeof(GLfloat)*3, NULL, GL_DYNAMIC_DRAW);
648                         
649                         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
650                         
651                         glGenTextures(1, &pmem.cuTexId);
652                         glBindTexture(GL_TEXTURE_2D, pmem.cuTexId);
653                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pmem.w, pmem.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
654                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
655                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
656                         glBindTexture(GL_TEXTURE_2D, 0);
657                         
658                         cuda_assert(cuGraphicsGLRegisterBuffer(&pmem.cuPBOresource, pmem.cuPBO, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE))
659
660                         cuda_pop_context();
661
662                         mem.device_pointer = pmem.cuTexId;
663                         pixel_mem_map[mem.device_pointer] = pmem;
664
665                         return;
666                 }
667
668                 Device::pixels_alloc(mem);
669         }
670
671         void pixels_copy_from(device_memory& mem, int y, int w, int h)
672         {
673                 if(!background) {
674                         PixelMem pmem = pixel_mem_map[mem.device_pointer];
675
676                         cuda_push_context();
677
678                         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO);
679                         uchar *pixels = (uchar*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_ONLY);
680                         size_t offset = sizeof(uchar)*4*y*w;
681                         memcpy((uchar*)mem.data_pointer + offset, pixels + offset, sizeof(uchar)*4*w*h);
682                         glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
683                         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
684
685                         cuda_pop_context();
686
687                         return;
688                 }
689
690                 Device::pixels_copy_from(mem, y, w, h);
691         }
692
693         void pixels_free(device_memory& mem)
694         {
695                 if(mem.device_pointer) {
696                         if(!background) {
697                                 PixelMem pmem = pixel_mem_map[mem.device_pointer];
698
699                                 cuda_push_context();
700
701                                 cuda_assert(cuGraphicsUnregisterResource(pmem.cuPBOresource))
702                                 glDeleteBuffers(1, &pmem.cuPBO);
703                                 glDeleteTextures(1, &pmem.cuTexId);
704
705                                 cuda_pop_context();
706
707                                 pixel_mem_map.erase(pixel_mem_map.find(mem.device_pointer));
708                                 mem.device_pointer = 0;
709
710                                 return;
711                         }
712
713                         Device::pixels_free(mem);
714                 }
715         }
716
717         void draw_pixels(device_memory& mem, int y, int w, int h, int width, int height, bool transparent)
718         {
719                 if(!background) {
720                         PixelMem pmem = pixel_mem_map[mem.device_pointer];
721
722                         cuda_push_context();
723
724                         /* for multi devices, this assumes the ineffecient method that we allocate
725                            all pixels on the device even though we only render to a subset */
726                         size_t offset = sizeof(uint8_t)*4*y*w;
727
728                         glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pmem.cuPBO);
729                         glBindTexture(GL_TEXTURE_2D, pmem.cuTexId);
730                         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)offset);
731                         glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
732                         
733                         glEnable(GL_TEXTURE_2D);
734                         
735                         if(transparent) {
736                                 glEnable(GL_BLEND);
737                                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
738                         }
739
740                         glColor3f(1.0f, 1.0f, 1.0f);
741
742                         glPushMatrix();
743                         glTranslatef(0.0f, (float)y, 0.0f);
744                                 
745                         glBegin(GL_QUADS);
746                         
747                         glTexCoord2f(0.0f, 0.0f);
748                         glVertex2f(0.0f, 0.0f);
749                         glTexCoord2f((float)w/(float)pmem.w, 0.0f);
750                         glVertex2f((float)width, 0.0f);
751                         glTexCoord2f((float)w/(float)pmem.w, (float)h/(float)pmem.h);
752                         glVertex2f((float)width, (float)height);
753                         glTexCoord2f(0.0f, (float)h/(float)pmem.h);
754                         glVertex2f(0.0f, (float)height);
755
756                         glEnd();
757
758                         glPopMatrix();
759
760                         if(transparent)
761                                 glDisable(GL_BLEND);
762                         
763                         glBindTexture(GL_TEXTURE_2D, 0);
764                         glDisable(GL_TEXTURE_2D);
765
766                         cuda_pop_context();
767
768                         return;
769                 }
770
771                 Device::draw_pixels(mem, y, w, h, width, height, transparent);
772         }
773
774         void task_add(DeviceTask& task)
775         {
776                 if(task.type == DeviceTask::TONEMAP)
777                         tonemap(task);
778                 else if(task.type == DeviceTask::PATH_TRACE)
779                         path_trace(task);
780                 else if(task.type == DeviceTask::DISPLACE)
781                         displace(task);
782         }
783
784         void task_wait()
785         {
786                 cuda_push_context();
787
788                 cuda_assert(cuCtxSynchronize())
789
790                 cuda_pop_context();
791         }
792
793         void task_cancel()
794         {
795         }
796 };
797
798 Device *device_cuda_create(bool background)
799 {
800         return new CUDADevice(background);
801 }
802
803 CCL_NAMESPACE_END
804