Code refactor: move more memory allocation logic into device API.
[blender.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_WRITE_ONLY,
37         MEM_READ_WRITE,
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         device_ptr data_pointer;
187         size_t data_size;
188         size_t device_size;
189         size_t data_width;
190         size_t data_height;
191         size_t data_depth;
192         MemoryType type;
193         const char *name;
194         InterpolationType interpolation;
195         ExtensionType extension;
196
197         /* Device pointer. */
198         Device *device;
199         device_ptr device_pointer;
200
201         virtual ~device_memory();
202
203 protected:
204         /* Only create through subclasses. */
205         device_memory(Device *device, const char *name, MemoryType type);
206
207         /* No copying allowed. */
208         device_memory(const device_memory&);
209         device_memory& operator = (const device_memory&);
210
211         /* Host allocation on the device. All data_pointer memory should be
212          * allocated with these functions, for devices that support using
213          * the same pointer for host and device. */
214         device_ptr host_alloc(size_t size);
215         void host_free(device_ptr ptr, size_t size);
216
217         /* Device memory allocation and copying. */
218         void device_alloc();
219         void device_free();
220         void device_copy_to();
221         void device_copy_from(int y, int w, int h, int elem);
222         void device_zero();
223 };
224
225 /* Device Only Memory
226  *
227  * Working memory only needed by the device, with no corresponding allocation
228  * on the host. Only used internally in the device implementations. */
229
230 template<typename T>
231 class device_only_memory : public device_memory
232 {
233 public:
234         device_only_memory(Device *device, const char *name)
235         : device_memory(device, name, MEM_READ_WRITE)
236         {
237                 data_type = device_type_traits<T>::data_type;
238                 data_elements = max(device_type_traits<T>::num_elements, 1);
239         }
240
241         virtual ~device_only_memory()
242         {
243                 free();
244         }
245
246         void alloc_to_device(size_t num)
247         {
248                 data_size = num*sizeof(T);
249                 device_alloc();
250         }
251
252         void free()
253         {
254                 device_free();
255         }
256
257         void zero_to_device()
258         {
259                 device_zero();
260         }
261 };
262
263 /* Device Vector
264  *
265  * Data vector to exchange data between host and device. Memory will be
266  * allocated on the host first with alloc() and resize, and then filled
267  * in and copied to the device with copy_to_device(). Or alternatively
268  * allocated and set to zero on the device with zero_to_device().
269  *
270  * When using memory type MEM_TEXTURE, a pointer to this memory will be
271  * automatically attached to kernel globals, using the provided name
272  * matching an entry in kernel_textures.h. */
273
274 template<typename T> class device_vector : public device_memory
275 {
276 public:
277         device_vector(Device *device, const char *name, MemoryType type)
278         : device_memory(device, name, type)
279         {
280                 data_type = device_type_traits<T>::data_type;
281                 data_elements = device_type_traits<T>::num_elements;
282
283                 assert(data_elements > 0);
284         }
285
286         virtual ~device_vector()
287         {
288                 free();
289         }
290
291         /* Host memory allocation. */
292         T *alloc(size_t width, size_t height = 0, size_t depth = 0)
293         {
294                 size_t new_size = size(width, height, depth);
295
296                 if(new_size != data_size) {
297                         device_free();
298                         host_free(data_pointer, sizeof(T)*data_size);
299                         data_pointer = host_alloc(sizeof(T)*new_size);
300                 }
301
302                 data_size = new_size;
303                 data_width = width;
304                 data_height = height;
305                 data_depth = depth;
306                 assert(device_ptr == 0);
307
308                 return get_data();
309         }
310
311         /* Host memory resize. Only use this if the original data needs to be
312          * preserved, it is faster to call alloc() if it can be discarded. */
313         T *resize(size_t width, size_t height = 0, size_t depth = 0)
314         {
315                 size_t new_size = size(width, height, depth);
316
317                 if(new_size != data_size) {
318                         device_ptr new_ptr = host_alloc(sizeof(T)*new_size);
319
320                         if(new_size && data_size) {
321                                 size_t min_size = ((new_size < data_size)? new_size: data_size);
322                                 memcpy((T*)new_ptr, (T*)data_pointer, sizeof(T)*min_size);
323                         }
324
325                         device_free();
326                         host_free(data_pointer, sizeof(T)*data_size);
327                         data_pointer = new_ptr;
328                 }
329
330                 data_size = new_size;
331                 data_width = width;
332                 data_height = height;
333                 data_depth = depth;
334                 assert(device_ptr == 0);
335
336                 return get_data();
337         }
338
339         /* Take over data from an existing array. */
340         void steal_data(array<T>& from)
341         {
342                 device_free();
343                 host_free(data_pointer, sizeof(T)*data_size);
344
345                 data_size = from.size();
346                 data_width = 0;
347                 data_height = 0;
348                 data_depth = 0;
349                 data_pointer = (device_ptr)from.steal_pointer();
350                 assert(device_pointer == 0);
351         }
352
353         /* Free device and host memory. */
354         void free()
355         {
356                 device_free();
357                 host_free(data_pointer, sizeof(T)*data_size);
358
359                 data_size = 0;
360                 data_width = 0;
361                 data_height = 0;
362                 data_depth = 0;
363                 data_pointer = 0;
364                 assert(device_pointer == 0);
365         }
366
367         size_t size()
368         {
369                 return data_size;
370         }
371
372         T* get_data()
373         {
374                 return (T*)data_pointer;
375         }
376
377         T& operator[](size_t i)
378         {
379                 assert(i < data_size);
380                 return get_data()[i];
381         }
382
383         void copy_to_device()
384         {
385                 device_copy_to();
386         }
387
388         void copy_from_device(int y, int w, int h)
389         {
390                 device_copy_from(y, w, h, sizeof(T));
391         }
392
393         void zero_to_device()
394         {
395                 device_zero();
396         }
397
398 protected:
399         size_t size(size_t width, size_t height, size_t depth)
400         {
401                 return width * ((height == 0)? 1: height) * ((depth == 0)? 1: depth);
402         }
403 };
404
405 /* Pixel Memory
406  *
407  * Device memory to efficiently draw as pixels to the screen in interactive
408  * rendering. Only copying pixels from the device is supported, not copying to. */
409
410 template<typename T> class device_pixels : public device_vector<T>
411 {
412 public:
413         device_pixels(Device *device, const char *name)
414         : device_vector<T>(device, name, MEM_PIXELS)
415         {
416         }
417
418         void alloc_to_device(size_t width, size_t height, size_t depth = 0)
419         {
420                 device_vector<T>::alloc(width, height, depth);
421                 device_memory::device_alloc();
422         }
423
424         T *copy_from_device(int y, int w, int h)
425         {
426                 device_memory::device_copy_from(y, w, h, sizeof(T));
427                 return device_vector<T>::get_data();
428         }
429 };
430
431 /* Device Sub Memory
432  *
433  * Pointer into existing memory. It is not allocated separately, but created
434  * from an already allocated base memory. It is freed automatically when it
435  * goes out of scope, which should happen before base memory is freed.
436  *
437  * Note: some devices require offset and size of the sub_ptr to be properly
438  * aligned to device->mem_address_alingment(). */
439
440 class device_sub_ptr
441 {
442 public:
443         device_sub_ptr(device_memory& mem, int offset, int size);
444         ~device_sub_ptr();
445
446         device_ptr operator*() const
447         {
448                 return ptr;
449         }
450
451 protected:
452         /* No copying. */
453         device_sub_ptr& operator = (const device_sub_ptr&);
454
455         Device *device;
456         device_ptr ptr;
457 };
458
459 CCL_NAMESPACE_END
460
461 #endif /* __DEVICE_MEMORY_H__ */
462