2a0279170669768b5a55ffc17ceff6c317abf7c9
[blender-staging.git] / intern / cycles / device / device_memory.h
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 #ifndef __DEVICE_MEMORY_H__
18 #define __DEVICE_MEMORY_H__
19
20 /* Device Memory
21  *
22  * Data types for allocating, copying and freeing device memory. */
23
24 #include "util/util_debug.h"
25 #include "util/util_half.h"
26 #include "util/util_texture.h"
27 #include "util/util_types.h"
28 #include "util/util_vector.h"
29
30 CCL_NAMESPACE_BEGIN
31
32 class Device;
33
34 enum MemoryType {
35         MEM_READ_ONLY,
36         MEM_READ_WRITE,
37         MEM_DEVICE_ONLY,
38         MEM_TEXTURE,
39         MEM_PIXELS
40 };
41
42 /* Supported Data Types */
43
44 enum DataType {
45         TYPE_UNKNOWN,
46         TYPE_UCHAR,
47         TYPE_UINT,
48         TYPE_INT,
49         TYPE_FLOAT,
50         TYPE_HALF,
51         TYPE_UINT64,
52 };
53
54 static inline size_t datatype_size(DataType datatype) 
55 {
56         switch(datatype) {
57                 case TYPE_UNKNOWN: return 1;
58                 case TYPE_UCHAR: return sizeof(uchar);
59                 case TYPE_FLOAT: return sizeof(float);
60                 case TYPE_UINT: return sizeof(uint);
61                 case TYPE_INT: return sizeof(int);
62                 case TYPE_HALF: return sizeof(half);
63                 case TYPE_UINT64: return sizeof(uint64_t);
64                 default: return 0;
65         }
66 }
67
68 /* Traits for data types */
69
70 template<typename T> struct device_type_traits {
71         static const DataType data_type = TYPE_UNKNOWN;
72         static const int num_elements = sizeof(T);
73 };
74
75 template<> struct device_type_traits<uchar> {
76         static const DataType data_type = TYPE_UCHAR;
77         static const int num_elements = 1;
78 };
79
80 template<> struct device_type_traits<uchar2> {
81         static const DataType data_type = TYPE_UCHAR;
82         static const int num_elements = 2;
83 };
84
85 template<> struct device_type_traits<uchar3> {
86         static const DataType data_type = TYPE_UCHAR;
87         static const int num_elements = 3;
88 };
89
90 template<> struct device_type_traits<uchar4> {
91         static const DataType data_type = TYPE_UCHAR;
92         static const int num_elements = 4;
93 };
94
95 template<> struct device_type_traits<uint> {
96         static const DataType data_type = TYPE_UINT;
97         static const int num_elements = 1;
98 };
99
100 template<> struct device_type_traits<uint2> {
101         static const DataType data_type = TYPE_UINT;
102         static const int num_elements = 2;
103 };
104
105 template<> struct device_type_traits<uint3> {
106         static const DataType data_type = TYPE_UINT;
107         static const int num_elements = 3;
108 };
109
110 template<> struct device_type_traits<uint4> {
111         static const DataType data_type = TYPE_UINT;
112         static const int num_elements = 4;
113 };
114
115 template<> struct device_type_traits<int> {
116         static const DataType data_type = TYPE_INT;
117         static const int num_elements = 1;
118 };
119
120 template<> struct device_type_traits<int2> {
121         static const DataType data_type = TYPE_INT;
122         static const int num_elements = 2;
123 };
124
125 template<> struct device_type_traits<int3> {
126         static const DataType data_type = TYPE_INT;
127         static const int num_elements = 3;
128 };
129
130 template<> struct device_type_traits<int4> {
131         static const DataType data_type = TYPE_INT;
132         static const int num_elements = 4;
133 };
134
135 template<> struct device_type_traits<float> {
136         static const DataType data_type = TYPE_FLOAT;
137         static const int num_elements = 1;
138 };
139
140 template<> struct device_type_traits<float2> {
141         static const DataType data_type = TYPE_FLOAT;
142         static const int num_elements = 2;
143 };
144
145 template<> struct device_type_traits<float3> {
146         static const DataType data_type = TYPE_FLOAT;
147         static const int num_elements = 4;
148 };
149
150 template<> struct device_type_traits<float4> {
151         static const DataType data_type = TYPE_FLOAT;
152         static const int num_elements = 4;
153 };
154
155 template<> struct device_type_traits<half> {
156         static const DataType data_type = TYPE_HALF;
157         static const int num_elements = 1;
158 };
159
160 template<> struct device_type_traits<half4> {
161         static const DataType data_type = TYPE_HALF;
162         static const int num_elements = 4;
163 };
164
165 template<> struct device_type_traits<uint64_t> {
166         static const DataType data_type = TYPE_UINT64;
167         static const int num_elements = 1;
168 };
169
170 /* Device Memory
171  *
172  * Base class for all device memory. This should not be allocated directly,
173  * instead the appropriate subclass can be used. */
174
175 class device_memory
176 {
177 public:
178         size_t memory_size() { return data_size*data_elements*datatype_size(data_type); }
179         size_t memory_elements_size(int elements) {
180                 return elements*data_elements*datatype_size(data_type);
181         }
182
183         /* Data information. */
184         DataType data_type;
185         int data_elements;
186         size_t data_size;
187         size_t device_size;
188         size_t data_width;
189         size_t data_height;
190         size_t data_depth;
191         MemoryType type;
192         const char *name;
193         InterpolationType interpolation;
194         ExtensionType extension;
195
196         /* Pointers. */
197         Device *device;
198         device_ptr device_pointer;
199         void *host_pointer;
200         void *shared_pointer;
201
202         virtual ~device_memory();
203
204 protected:
205         friend class CUDADevice;
206
207         /* Only create through subclasses. */
208         device_memory(Device *device, const char *name, MemoryType type);
209
210         /* No copying allowed. */
211         device_memory(const device_memory&);
212         device_memory& operator = (const device_memory&);
213
214         /* Host allocation on the device. All host_pointer memory should be
215          * allocated with these functions, for devices that support using
216          * the same pointer for host and device. */
217         void *host_alloc(size_t size);
218         void host_free();
219
220         /* Device memory allocation and copying. */
221         void device_alloc();
222         void device_free();
223         void device_copy_to();
224         void device_copy_from(int y, int w, int h, int elem);
225         void device_zero();
226 };
227
228 /* Device Only Memory
229  *
230  * Working memory only needed by the device, with no corresponding allocation
231  * on the host. Only used internally in the device implementations. */
232
233 template<typename T>
234 class device_only_memory : public device_memory
235 {
236 public:
237         device_only_memory(Device *device, const char *name)
238         : device_memory(device, name, MEM_DEVICE_ONLY)
239         {
240                 data_type = device_type_traits<T>::data_type;
241                 data_elements = max(device_type_traits<T>::num_elements, 1);
242         }
243
244         virtual ~device_only_memory()
245         {
246                 free();
247         }
248
249         void alloc_to_device(size_t num, bool shrink_to_fit = true)
250         {
251                 size_t new_size = num*sizeof(T);
252                 bool reallocate;
253
254                 if(shrink_to_fit) {
255                         reallocate = (data_size != new_size);
256                 }
257                 else {
258                         reallocate = (data_size < new_size);
259                 }
260
261                 if(reallocate) {
262                         device_free();
263                         data_size = new_size;
264                         device_alloc();
265                 }
266         }
267
268         void free()
269         {
270                 device_free();
271                 data_size = 0;
272         }
273
274         void zero_to_device()
275         {
276                 device_zero();
277         }
278 };
279
280 /* Device Vector
281  *
282  * Data vector to exchange data between host and device. Memory will be
283  * allocated on the host first with alloc() and resize, and then filled
284  * in and copied to the device with copy_to_device(). Or alternatively
285  * allocated and set to zero on the device with zero_to_device().
286  *
287  * When using memory type MEM_TEXTURE, a pointer to this memory will be
288  * automatically attached to kernel globals, using the provided name
289  * matching an entry in kernel_textures.h. */
290
291 template<typename T> class device_vector : public device_memory
292 {
293 public:
294         device_vector(Device *device, const char *name, MemoryType type)
295         : device_memory(device, name, type)
296         {
297                 data_type = device_type_traits<T>::data_type;
298                 data_elements = device_type_traits<T>::num_elements;
299
300                 assert(data_elements > 0);
301         }
302
303         virtual ~device_vector()
304         {
305                 free();
306         }
307
308         /* Host memory allocation. */
309         T *alloc(size_t width, size_t height = 0, size_t depth = 0)
310         {
311                 size_t new_size = size(width, height, depth);
312
313                 if(new_size != data_size) {
314                         device_free();
315                         host_free();
316                         host_pointer = host_alloc(sizeof(T)*new_size);
317                         assert(device_pointer == 0);
318                 }
319
320                 data_size = new_size;
321                 data_width = width;
322                 data_height = height;
323                 data_depth = depth;
324
325                 return data();
326         }
327
328         /* Host memory resize. Only use this if the original data needs to be
329          * preserved, it is faster to call alloc() if it can be discarded. */
330         T *resize(size_t width, size_t height = 0, size_t depth = 0)
331         {
332                 size_t new_size = size(width, height, depth);
333
334                 if(new_size != data_size) {
335                         void *new_ptr = host_alloc(sizeof(T)*new_size);
336
337                         if(new_size && data_size) {
338                                 size_t min_size = ((new_size < data_size)? new_size: data_size);
339                                 memcpy((T*)new_ptr, (T*)host_pointer, sizeof(T)*min_size);
340                         }
341
342                         device_free();
343                         host_free();
344                         host_pointer = new_ptr;
345                         assert(device_pointer == 0);
346                 }
347
348                 data_size = new_size;
349                 data_width = width;
350                 data_height = height;
351                 data_depth = depth;
352
353                 return data();
354         }
355
356         /* Take over data from an existing array. */
357         void steal_data(array<T>& from)
358         {
359                 device_free();
360                 host_free();
361
362                 data_size = from.size();
363                 data_width = 0;
364                 data_height = 0;
365                 data_depth = 0;
366                 host_pointer = from.steal_pointer();
367                 assert(device_pointer == 0);
368         }
369
370         /* Free device and host memory. */
371         void free()
372         {
373                 device_free();
374                 host_free();
375
376                 data_size = 0;
377                 data_width = 0;
378                 data_height = 0;
379                 data_depth = 0;
380                 host_pointer = 0;
381                 assert(device_pointer == 0);
382         }
383
384         size_t size()
385         {
386                 return data_size;
387         }
388
389         T* data()
390         {
391                 return (T*)host_pointer;
392         }
393
394         T& operator[](size_t i)
395         {
396                 assert(i < data_size);
397                 return data()[i];
398         }
399
400         void copy_to_device()
401         {
402                 device_copy_to();
403         }
404
405         void copy_from_device(int y, int w, int h)
406         {
407                 device_copy_from(y, w, h, sizeof(T));
408         }
409
410         void zero_to_device()
411         {
412                 device_zero();
413         }
414
415 protected:
416         size_t size(size_t width, size_t height, size_t depth)
417         {
418                 return width * ((height == 0)? 1: height) * ((depth == 0)? 1: depth);
419         }
420 };
421
422 /* Pixel Memory
423  *
424  * Device memory to efficiently draw as pixels to the screen in interactive
425  * rendering. Only copying pixels from the device is supported, not copying to. */
426
427 template<typename T> class device_pixels : public device_vector<T>
428 {
429 public:
430         device_pixels(Device *device, const char *name)
431         : device_vector<T>(device, name, MEM_PIXELS)
432         {
433         }
434
435         void alloc_to_device(size_t width, size_t height, size_t depth = 0)
436         {
437                 device_vector<T>::alloc(width, height, depth);
438                 device_memory::device_alloc();
439         }
440
441         T *copy_from_device(int y, int w, int h)
442         {
443                 device_memory::device_copy_from(y, w, h, sizeof(T));
444                 return device_vector<T>::data();
445         }
446 };
447
448 /* Device Sub Memory
449  *
450  * Pointer into existing memory. It is not allocated separately, but created
451  * from an already allocated base memory. It is freed automatically when it
452  * goes out of scope, which should happen before base memory is freed.
453  *
454  * Note: some devices require offset and size of the sub_ptr to be properly
455  * aligned to device->mem_address_alingment(). */
456
457 class device_sub_ptr
458 {
459 public:
460         device_sub_ptr(device_memory& mem, int offset, int size);
461         ~device_sub_ptr();
462
463         device_ptr operator*() const
464         {
465                 return ptr;
466         }
467
468 protected:
469         /* No copying. */
470         device_sub_ptr& operator = (const device_sub_ptr&);
471
472         Device *device;
473         device_ptr ptr;
474 };
475
476 CCL_NAMESPACE_END
477
478 #endif /* __DEVICE_MEMORY_H__ */
479